nexu-io/open-design · error · Error
unsupported template binding path: ${rawPath}
Error message
unsupported template binding path: ${rawPath} What it means
Thrown by readTemplatePath when the first segment of a binding path (before the first '.') is not literally 'data'. All global bindings must root at the data object: {{data.title}}, {{data.items.0.name}}, etc. A path that does not start with 'data' is treated as unsupported at the root scope; loop-variable-scoped bindings are handled by a different resolver (childResolver) and never reach this check.
Source
Thrown at apps/daemon/src/live-artifacts/render.ts:73
function walkPath(root: unknown, segments: string[], rawPath: string): unknown {
let current: unknown = root;
for (const segment of segments) {
if (current === null || current === undefined) return '';
if (Array.isArray(current)) {
if (!/^\d+$/.test(segment)) throw new Error(`invalid array segment in template binding path: ${rawPath}`);
current = current[Number(segment)];
continue;
}
if (typeof current !== 'object') return '';
current = (current as Record<string, unknown>)[segment];
}
return current ?? '';
}
function readTemplatePath(dataJson: BoundedJsonObject, rawPath: string): unknown {
const segments = rawPath.split('.');
if (segments.shift() !== 'data') throw new Error(`unsupported template binding path: ${rawPath}`);
return walkPath(dataJson, segments, rawPath);
}
function scalarOrThrow(value: unknown, binding: string): string {
if (Array.isArray(value) || (value !== null && typeof value === 'object')) {
throw new Error(`template binding must resolve to a scalar: ${binding}`);
}
return escapeHtmlTemplateValue(value);
}
function rootResolver(dataJson: BoundedJsonObject): BindingResolver {
return (binding) => {
if (!TEMPLATE_PATH.test(binding) || !binding.startsWith('data')) {
throw new Error(`invalid template binding path: ${binding}`);
}
return scalarOrThrow(readTemplatePath(dataJson, binding), binding);
};
}View on GitHub (pinned to 5be4028344)
Solutions
- Prefix root bindings with 'data.': {{data.title}} instead of {{title}}.
- For loop content use a repeat directive and bind via the loop variable name: {{item.field}}.
Example fix
// before
<div>{{title}}</div>
// after
<div>{{data.title}}</div> Defensive patterns
Strategy: validation
Validate before calling
function assertRootedAtData(binding: string): void {
if (binding.split('.')[0] !== 'data') throw new Error(`binding must start with data.: ${binding}`);
} Type guard
function isDataRootedBinding(b: string): boolean { return b.startsWith('data.') || b === 'data'; } Prevention
- Always prefix root bindings with 'data.'.
- Reserve non-data heads for loop variables inside repeat blocks only.
- Add an authoring-time linter that flags bare-name bindings at the root.
When it happens
Trigger: Binding {{title}} (missing data. prefix); {{config.title}}; {{metadata.tags}}; any root binding whose head segment isn't 'data'. Note: child loop variables like {{item.label}} are valid inside a repeat because childResolver handles them first.
Common situations: Model omits the data. prefix (common in Handlebars/Mustache where the root is implicit); template ported from a framework that allows bare names; misunderstanding that only loop variables (inside a repeat) and 'data.*' are valid heads.
Related errors
- invalid array segment in template binding path: ${rawPath}
- invalid template binding path: ${binding}
- template binding must resolve to a scalar: ${binding}
- connector refresh source requires connector metadata
- ${field} must be a dot-separated JSON path
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/f5395a0dae6bcb4e.
Report an issue: GitHub.