{"record":{"id":"c5fc9d37aa1b4dae","repo":"nexu-io/open-design","slug":"raw-template-interpolation-is-not-supported","errorCode":null,"errorMessage":"raw template interpolation is not supported","messagePattern":"raw template interpolation is not supported","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/render.ts","lineNumber":269,"sourceCode":"    const itemTemplate = element.replace(REPEAT_DIRECTIVE, '');\n    if (findRepeatDirective(itemTemplate)) {\n      throw new Error('nested data-od-repeat is not supported');\n    }\n\n    out += interpolateScalars(html.slice(cursor, openTagStart), resolve);\n    for (const item of readArray(arrayPath)) {\n      out += renderFragment(itemTemplate, childResolver(resolve, varName, item), readArray);\n    }\n    cursor = elementEnd;\n  }\n  return out;\n}\n\nexport function renderHtmlTemplateV1(input: LiveArtifactRenderInput): LiveArtifactRenderOutput {\n  validateHtmlTemplateV1Security(input.templateHtml);\n\n  if (RAW_TEMPLATE_INTERPOLATION.test(input.templateHtml)) {\n    throw new Error('raw template interpolation is not supported');\n  }\n\n  const resolve = rootResolver(input.dataJson);\n  const readArray: ArrayReader = (arrayPath) => {\n    const value = readTemplatePath(input.dataJson, arrayPath);\n    if (!Array.isArray(value)) throw new Error(`data-od-repeat source is not an array: ${arrayPath}`);\n    return value;\n  };\n\n  return { html: renderFragment(input.templateHtml, resolve, readArray) };\n}\n","sourceCodeStart":251,"sourceCodeEnd":281,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/render.ts#L251-L281","documentation":"Thrown by renderHtmlTemplateV1() when the template contains a raw-interpolation construct — either triple-mustache `{{{ ... }}}` or ampersand-mustache `{{& ... }}`. These are Mustache/Handlebars conventions for inserting unescaped HTML, which the live artifact renderer rejects outright as an XSS control: all scalar substitution in html_template_v1 is auto-escaped via escapeHtmlTemplateValue(), and there is no opt-out.","triggerScenarios":"Template.html contains `{{{data.htmlBlob}}}` or `{{& data.htmlBlob}}` intending to inject raw HTML. Also triggered by an LLM that has seen Handlebars examples and reproduces the raw-insertion syntax.","commonSituations":"Migrating an existing Mustache/Handlebars template into a live artifact; agent trained on templating docs emits triple-brace; developer assumes raw HTML insertion is needed for rich text and reaches for the familiar `{{{ }}}`.","solutions":["Replace raw insertion with the standard escaped form `{{data.htmlBlob}}` — the value is HTML-escaped automatically, which is the intended behavior.","If you genuinely need rich HTML, pre-render it into the artifact's static preview HTML outside the template pipeline rather than injecting through a binding.","Remove any stray `{{{`/`}}}` or `{{&`/`}}` tokens from the template (e.g. documentation strings that literally show the syntax)."],"exampleFix":"// before\n<div>{{{data.description}}}</div>\n// after\n<div>{{data.description}}</div>","handlingStrategy":"validation","validationCode":"const RAW_TEMPLATE_INTERPOLATION = /{{{[^{}]*}}}|{{\\s*&[^{}]*}}/;\n\nfunction assertNoRawInterpolation(templateHtml: string): void {\n  if (RAW_TEMPLATE_INTERPOLATION.test(templateHtml)) {\n    throw new Error('template uses raw interpolation ({{{...}}} or {{& ...}}) — convert to escaped {{...}}');\n  }\n}\n\nassertNoRawInterpolation(input.templateHtml);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use double-brace `{{...}}` for auto-escaped substitution.","When porting a Mustache/Handlebars template, search-and-replace `{{{` → `{{` and `{{&` → `{{`.","Never insert untrusted HTML via a binding; if rich HTML is required, render it statically outside the template pipeline."],"tags":["live-artifacts","html-template","security","xss","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}