---
title: "Solución de problemas — Docs de zenit Zig UI"
description: "De los errores de build al ciclo de vida del estado y los fallos de renderizado: el camino más corto hasta la causa."
url: https://zenit.z.express/es/docs/reference/troubleshooting
language: es
alternate_en: https://zenit.z.express/docs/reference/troubleshooting.md
alternate_zh: https://zenit.z.express/zh/docs/reference/troubleshooting.md
alternate_ja: https://zenit.z.express/ja/docs/reference/troubleshooting.md
alternate_ko: https://zenit.z.express/ko/docs/reference/troubleshooting.md
alternate_fr: https://zenit.z.express/fr/docs/reference/troubleshooting.md
alternate_de: https://zenit.z.express/de/docs/reference/troubleshooting.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Solución de problemas

De los errores de build al ciclo de vida del estado y los fallos de renderizado: el camino más corto hasta la causa.

## Errores de build

**invalid fingerprint**

La fingerprint de una plantilla no se puede reutilizar. Borra la línea `.fingerprint` de `build.zig.zon`, ejecuta `zig build` una vez y vuelve a pegar el valor único que imprime Zig.

**import of file outside module path**

No ejecutes `zig test` directamente sobre archivos internos del framework. Usa los steps de prueba del repo, como `zig build test-ui` (consulta [Comandos](https://zenit.z.express/es/docs/reference/commands)).

**@import("ui") / @import("zenit_app") not found**

Comprueba que `build.zig.zon` declara la dependencia `.zenit` y que llamas a `zenit.attach(zenit_dep, exe)` después de crear el ejecutable: añade los módulos `ui` / `zenit_app`, compila los puentes de macOS y enlaza los frameworks del sistema.

**build API field mismatch**

Ejecuta primero `zig version`. zenit requiere Zig 0.15.2 (`minimum_zig_version` en `build.zig.zon`); las build API de otras versiones no están soportadas.

**-Dtest-mode=true has no effect**

Zig aísla las opciones de las dependencias. Reenvía `.@"test-mode"` y `.@"e2e-port"` explícitamente en `b.dependency("zenit", ...)`, como hace `templates/minimal-app/build.zig`.

## Ejecución y estado

**error.StateNotFound**

Un `cx.handler(State, id, method)` con id explícito se ejecutó antes de que existiera ese estado. Mejor usa `cx.bindState` + `cx.on`: pasan el puntero al estado directamente, así que no hay ids que gestionar.

**leaked ArrayList / HashMap at exit**

El estado de `bindState` pertenece al Cx y se libera junto con él; si T declara un `deinit(self: *T)` **pub**, el framework lo llama automáticamente. Una fuga suele indicar que `deinit` no es pub o que el recurso debía seguir el ciclo de vida de una página. Consulta la siguiente sección.

**crash after switching pages**

Busca un `*Node` o `*Scope` de la página anterior guardado en estado global. Cuando el Scope de la página se libera, esos punteros dejan de ser válidos y deben limpiarse junto con él.

**Invalid free after updating text**

El texto actual del nodo puede ser una copia que el propio nodo posee. No edites `content` en las props obtenidas con `getText()` para volver a pasarlas a `setText`; usa `try node.setTextContent(cx.allocator, "Updated")`, que copia la cadena y marca la propiedad correctamente.

## Limpiar el estado correctamente

Guarda un campo allocator en el estado y deja que un `deinit` pub haga la limpieza. El valor inicial todavía no debe poseer recursos: asigna después de `bindState`.

`state_deinit.zig`

```zig
const Editor = struct {
    allocator: std.mem.Allocator,
    lines: std.ArrayList([]const u8) = .empty,

    // pub: the Cx calls this automatically when it frees the state.
    pub fn deinit(self: *Editor) void {
        self.lines.deinit(self.allocator);
    }
};

// The initial value must not own resources yet; allocate after binding.
const editor = try cx.bindState(Editor, .{ .allocator = cx.allocator });
```

Recurre a `scope.onCleanup` solo cuando la limpieza no es un `deinit` pub, o cuando el recurso debe seguir a un Scope (una página) y no a todo el Cx.

`scope_cleanup.zig`

```zig
// Resources that must follow a page (Scope), not the whole window (Cx).
const PageCache = struct {
    allocator: std.mem.Allocator,
    map: std.StringHashMapUnmanaged(u32) = .empty,

    // Not named `pub fn deinit`: the Cx would call it again and double-free.
    fn release(self: *PageCache) void {
        self.map.deinit(self.allocator);
    }
};

const cache = try cx.bindState(PageCache, .{ .allocator = cx.allocator });
try scope.onCleanup(PageCache, cache, PageCache.release);
```

> WARNING
> 
> **No registres deinit dos veces.** Si T ya tiene un `deinit` pub, registrar la misma función con `scope.onCleanup` provoca una doble liberación: una al liberarse el Scope y otra cuando el Cx libera el estado.

## Layout y dibujo

**state changed but the text did not**

`setText` ya compara las firmas antigua y nueva y marca sizing / render dirty cuando hace falta: no necesitas `markRenderDirty` manual. Un texto desactualizado suele indicar que se editó una copia de `TextProps` sin llamar a `setText`, o que el valor mostrado nunca se suscribió a su Signal. Mejor usa `ui.textFmt(cx, scope, fmt, .{ signals }, props)` para suscribirte directamente a Signals / Memos.

**some colors ignore a theme switch**

`boxStyled` / `textStyled` y compañía adjuntan un hook `on_theme`, y `cx.setTheme` solo los vuelve a ejecutar en el subárbol de `cx.root`; los nodos simples y los estilos de componentes son instantáneas tomadas en el mount. Reconstruye esos árboles al cambiar de tema, o suscríbete a `cx.themeSignal(scope)` en un Effect y reaplica los estilos; los nodos que no cuelgan de `cx.root` tampoco se actualizan.

**clicks fall through an overlay’s empty area**

Los contenedores puramente visuales sin interacción son `pass_through` por defecto. Define `hit_behavior = .@"opaque"` en la extensión de estilo de la raíz del overlay, o usa [Modal](https://zenit.z.express/es/components/modal) / [Sheet](https://zenit.z.express/es/components/sheet), que traen su propia barrera.

**a huge invisible hit area in the window**

Adjunta `ui.devtools.overlay` para inspeccionar el nodo alcanzado e imprime su rect en pantalla con `node.globalRect()`. La causa habitual es un sizing fill / grow del padre o un posicionamiento absoluto demasiado grande; en los overlays ocultos, comprueba que estén fuera del hit-testing: un subárbol oculto con `node.setDisplay(.none)` sale por completo del layout, el pintado, el hit-testing y el recorrido con Tab.

`opaque_overlay.zig`

```zig
// Plain visual containers are pass-through by default.
// Make an overlay root swallow clicks on its empty area:
(try panel.style.ensureExtFallible(cx.allocator)).hit_behavior = .@"opaque";
```

## ¿Sigues atascado?

Cuando abras un issue, incluye como mínimo:

-   **Versiones**: la revisión de zenit, `zig version` y tu versión de macOS.
    
-   **Reproducción**: un ejemplo mínimo reproducible y el build step que ejecutaste.
    
-   **Salida**: la salida de error completa, no solo la última línea.
    
-   **Problemas de renderizado**: una captura de pantalla y los tamaños de los nodos implicados según DevTools.
