$ rezi
Guides

Debugging Constraints

Rezi constraint failures are deterministic: there are no silent fallbacks and no “best effort” coercions.

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:

  1. Search for expr(" usages near the failing widget
  2. Check for #id.* references and confirm the referenced ids exist in the same committed tree
  3. Confirm direct #id.* targets are unique (use max_sibling / sum_sibling for shared ids)
  4. For max_sibling / sum_sibling, confirm the matching ids are siblings under the same parent as the consumer
  5. 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 uses clamp(min, value, max).
  • Assuming boolean operators exist: the DSL does not support &&/||; use if(...) or ternary.
  • Creating “invisible dependencies”: referencing a widget that is conditionally omitted via show(...) can invalidate sibling refs.

On this page