{"record":{"id":"230b099616342e4f","repo":"nexu-io/open-design","slug":"template-binding-must-resolve-to-a-scalar-bindi","errorCode":null,"errorMessage":"template binding must resolve to a scalar: ${binding}","messagePattern":"template binding must resolve to a scalar: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/render.ts","lineNumber":79,"sourceCode":"      if (!/^\\d+$/.test(segment)) throw new Error(`invalid array segment in template binding path: ${rawPath}`);\n      current = current[Number(segment)];\n      continue;\n    }\n    if (typeof current !== 'object') return '';\n    current = (current as Record<string, unknown>)[segment];\n  }\n  return current ?? '';\n}\n\nfunction readTemplatePath(dataJson: BoundedJsonObject, rawPath: string): unknown {\n  const segments = rawPath.split('.');\n  if (segments.shift() !== 'data') throw new Error(`unsupported template binding path: ${rawPath}`);\n  return walkPath(dataJson, segments, rawPath);\n}\n\nfunction scalarOrThrow(value: unknown, binding: string): string {\n  if (Array.isArray(value) || (value !== null && typeof value === 'object')) {\n    throw new Error(`template binding must resolve to a scalar: ${binding}`);\n  }\n  return escapeHtmlTemplateValue(value);\n}\n\nfunction rootResolver(dataJson: BoundedJsonObject): BindingResolver {\n  return (binding) => {\n    if (!TEMPLATE_PATH.test(binding) || !binding.startsWith('data')) {\n      throw new Error(`invalid template binding path: ${binding}`);\n    }\n    return scalarOrThrow(readTemplatePath(dataJson, binding), binding);\n  };\n}\n\nfunction childResolver(parent: BindingResolver, varName: string, item: unknown): BindingResolver {\n  return (binding) => {\n    if (!TEMPLATE_PATH.test(binding)) throw new Error(`invalid template binding path: ${binding}`);\n    const segments = binding.split('.');\n    if (segments[0] !== varName) return parent(binding);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/render.ts#L61-L97","documentation":"Thrown by scalarOrThrow when a resolved binding value is an Array or a non-null object. Live artifact interpolation only substitutes scalar values (string, number, boolean, null); binding a path that resolves to a structured value would either render '[object Object]' / JSON spam, or invite unsafe serialization, so the render rejects it and asks the author to drill down to a scalar.","triggerScenarios":"{{data.items}} where data.items is an array; {{data.user}} where data.user is an object; {{data.repo}} bound to the full repository object from the GitHub metric instead of a single field like {{data.repo.full_name}}.","commonSituations":"Model binds a whole sub-object expecting JSON pretty-printing; data shape expanded (a flat field became nested); forgetting to select a leaf field from the refresh source output.","solutions":["Drill the path to a scalar leaf: {{data.user.name}} instead of {{data.user}}.","For arrays, iterate with a repeat directive and bind a scalar inside the loop body.","If you must surface structured data, select individual scalar fields in the refresh source rather than dumping the object."],"exampleFix":"// before\n<div>{{data.user}}</div>\n// after\n<div>{{data.user.name}} ({{data.user.email}})</div>","handlingStrategy":"type-guard","validationCode":"function isScalarLeaf(v: unknown): v is string | number | boolean | null {\n  return v === null || ['string','number','boolean'].includes(typeof v);\n}","typeGuard":"function isScalar(v: unknown): v is string | number | boolean | null {\n  return v === null || typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';\n}","tryCatchPattern":null,"preventionTips":["Bind only leaf scalar fields; arrays/objects are not rendered.","Project nested objects down to scalar fields in the refresh source output.","Use the repeat directive to iterate arrays instead of binding them whole."],"tags":["template-binding","type-check","live-artifacts","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}