nexu-io/open-design · error · Error

invalid data-od-repeat directive: "${directive.spec}" (expec

Error message

invalid data-od-repeat directive: "${directive.spec}" (expected "item in data.path")

What it means

Thrown by renderFragment() when the contents of a data-od-repeat attribute do not match the required directive grammar: `varName in data.path`. The REPEAT_DIRECTIVE_SPEC regex demands exactly one loop variable (identifier), the literal keyword `in`, and a data-rooted dotted path (must start with `data`). Any deviation — missing `in`, missing `data.` prefix, disallowed characters, multiple variables — is rejected.

Source

Thrown at apps/daemon/src/live-artifacts/render.ts:238

    }
    cursor = matchEnd;
  }
  return null;
}

function renderFragment(html: string, resolve: BindingResolver, readArray: ArrayReader): string {
  let out = '';
  let cursor = 0;
  while (cursor < html.length) {
    const directive = findRepeatDirective(html, cursor);
    if (!directive) {
      out += interpolateScalars(html.slice(cursor), resolve);
      break;
    }
    const { openTagStart, tagName } = directive;

    const spec = REPEAT_DIRECTIVE_SPEC.exec(directive.spec);
    if (!spec?.[1] || !spec[2]) throw new Error(`invalid data-od-repeat directive: "${directive.spec}" (expected "item in data.path")`);
    const varName = spec[1];
    const arrayPath = spec[2];

    const openTagEnd = findTagEnd(html, openTagStart);
    const selfClosed = html[openTagEnd - 1] === '/';
    const elementEnd = selfClosed ? openTagEnd + 1 : findElementEnd(html, tagName, openTagEnd + 1);
    const element = html.slice(openTagStart, elementEnd);

    // Strip only this element's own directive from its opening tag; a REAL
    // directive anywhere in what remains means a nested repeat, which the
    // contract does not support. Literal mentions inside the repeated body
    // stay inert — same rule as the top-level scan.
    const itemTemplate = element.replace(REPEAT_DIRECTIVE, '');
    if (findRepeatDirective(itemTemplate)) {
      throw new Error('nested data-od-repeat is not supported');
    }

    out += interpolateScalars(html.slice(cursor, openTagStart), resolve);

View on GitHub (pinned to 5be4028344)

Solutions

  1. Rewrite the directive as `data-od-repeat="<varName> in data.<path>"`, e.g. `data-od-repeat="item in data.items"`.
  2. Make sure the source path begins with `data.` and uses only allowed segment characters (letters, digits, underscore, dash; numeric indices allowed).
  3. Use a single loop variable name made of `[A-Za-z_][A-Za-z0-9_]*` (no dashes).
  4. If you need the index, expose it inside data.json (e.g. precompute `data.indexedItems` with explicit index fields) since the directive grammar does not support `(item, i) in ...`.

Example fix

// before
<li data-od-repeat="item in items">{{item.label}}</li>
// after
<li data-od-repeat="item in data.items">{{item.label}}</li>
Defensive patterns

Strategy: validation

Validate before calling

const REPEAT_DIRECTIVE_SPEC = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+in\s+(data(?:\.(?:[A-Za-z_][A-Za-z0-9_-]*|\d+))*)\s*$/;

function isValidRepeatDirective(spec: string): boolean {
  const m = REPEAT_DIRECTIVE_SPEC.exec(spec);
  return !!m && !!m[1] && !!m[2];
}

// scan the template before rendering
for (const [, spec] of templateHtml.matchAll(/\bdata-od-repeat\s*=\s*"([^"]*)"/gi)) {
  if (!isValidRepeatDirective(spec)) throw new Error(`bad directive: ${spec}`);
}

Type guard

function isRepeatDirectiveSpec(spec: string): spec is `${string} in data.${string}` {
  return /^\s*[A-Za-z_][A-Za-z0-9_]*\s+in\s+data(?:\.(?:[A-Za-z_][A-Za-z0-9_-]*|\d+))+\s*$/.test(spec);
}

Prevention

When it happens

Trigger: Writing `data-od-repeat="item in items"` (forgot the `data.` prefix on the source); `data-od-repeat="items"` (missing the `in` keyword); `data-od-repeat="item, idx in data.items"` (multiple loop vars not supported); `data-od-repeat="item in data.items.list"` is fine but `data-od-repeat="item-in data.items"` (dash in var name) is not.

Common situations: Developer copies a Vue (`v-for="item in items"`) or Angular (`*ngFor="let item of items"`) snippet without translating to the html_template_v1 `data.*` convention; LLM authors a directive from memory and omits the `data.` prefix; using `of` instead of `in`.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/e278044b38c245f0. Report an issue: GitHub.