Debugging Constraints¶
Rezi constraint failures are deterministic: there are no silent fallbacks and no “best effort” coercions.
This guide is a diagnosis flow for the most common constraint/layout failures.
See also:
- docs/guide/constraints.md
- docs/reference/constraint-expressions.md
- docs/dev/live-pty-debugging.md (frame-audit workflow)
1) Identify the error class¶
ConstraintSyntaxError (parse-time)¶
Cause: invalid DSL syntax in expr("...").
Fix: - Correct the expression string - Prefer helper constraints to avoid hand-typed syntax for common patterns
ZRUI_INVALID_CONSTRAINT (frame-time)¶
Common causes:
- Unknown function name (typo): clmp(...)
- Unknown widget ref: #sidebr.w
- Ambiguous direct widget ref when ids are shared
- Invalid max_sibling / sum_sibling usage
Fix:
- Check the expression source in the error detail
- If you intended a common pattern, replace it with a helper from docs/reference/constraints-api.md
ZRUI_CIRCULAR_CONSTRAINT (frame-time)¶
Cause: a cycle in dependency graph (often mutual sibling width references).
Fix:
- Anchor one side of the relationship to parent.*, viewport.*, or intrinsic.*
- Avoid #a → #b and #b → #a loops
ZRUI_INVALID_PROPS¶
Cause: prop contract violations, for example:
- % size strings
- responsive-map layout constraints ({ sm, md, ... })
- grid.columns: expr(...) (invalid in alpha)
Fix:
- Migrate to helper constraints / expr where supported
- Use multiple grids and switch with display for responsive columns
2) Narrow down the source¶
Practical steps:
- Search for
expr("usages near the failing widget - Check for
#id.*references and confirm the referenced ids exist in the same committed tree - Confirm direct
#id.*targets are unique (usemax_sibling/sum_siblingfor shared ids) - For
max_sibling/sum_sibling, confirm the matching ids are siblings under the same parent as the consumer - Reduce the expression to a simpler form to isolate which term is invalid
3) Use deterministic runtime evidence (frame audit)¶
For hard-to-reproduce visual glitches, use the PTY + frame-audit workflow:
REZI_FRAME_AUDIT=1 REZI_FRAME_AUDIT_LOG=/tmp/rezi-frame-audit.ndjson node <your-app>
node scripts/frame-audit-report.mjs /tmp/rezi-frame-audit.ndjson --latest-pid
This helps confirm whether layout/renderer diverges across frames, and provides stage-level counters.
4) Use the dev inspector overlay (constraints instrumentation)¶
Rezi can surface constraint graph + resolved values in the dev inspector overlay.
Enable it by using createAppWithInspectorOverlay(...) (default toggle hotkey is ctrl+shift+i):
import { createAppWithInspectorOverlay } from "@rezi-ui/core";
import { createNodeBackend } from "@rezi-ui/node";
const app = createAppWithInspectorOverlay({
backend: createNodeBackend(),
initialState: { /* ... */ },
});
What you’ll see:
- constraints: graph node count, hidden-by-display count, and whether the last resolution was reused / cache hit / computed.
- constraints_focus: resolved constraint values for the currently focused widget id (if present in the constraint graph).
- constraints_exprs: the constraint expressions that apply to that focused widget instance (truncated).
Notes:
- If an id is shared by multiple widgets, the overlay reports instances=N and shows one instance. Prefer unique ids when debugging.
- This instrumentation is intended for development (it adds per-frame bookkeeping and allocations).
5) Recognize common constraint bugs¶
- Wrong
clamp(...)argument order: Rezi usesclamp(min, value, max). - Assuming boolean operators exist: the DSL does not support
&&/||; useif(...)or ternary. - Creating “invisible dependencies”: referencing a widget that is conditionally omitted via
show(...)can invalidate sibling refs.