{"record":{"id":"5840ffd89239416b","repo":"nexu-io/open-design","slug":"data-od-repeat-source-is-not-an-array-arraypath","errorCode":null,"errorMessage":"data-od-repeat source is not an array: ${arrayPath}","messagePattern":"data-od-repeat source is not an array: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/render.ts","lineNumber":275,"sourceCode":"    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":257,"sourceCodeEnd":281,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/render.ts#L257-L281","documentation":"Thrown by the readArray closure inside renderHtmlTemplateV1() when the data-od-repeat source path resolves to a value that is not an Array. readTemplatePath() walks the data.json object for the path; if the resulting value is anything other than an Array (string, number, object, null, undefined), the repeat cannot iterate and the renderer aborts. An undefined/missing path resolves to '' (empty string) via walkPath, which is also not an array.","triggerScenarios":"Pointing the repeat at a scalar field: `data-od-repeat=\"item in data.title\"` where `data.title` is a string. Pointing at an object: `data-od-repeat=\"item in data.config\"`. Pointing at a path that does not exist in data.json (resolves to '', not an array). Pointing at null.","commonSituations":"Schema drift between the agent's mental model and the actual data.json shape; typo in the path; data field renamed but template not updated; data.json missing the expected array key because the upstream producer emitted an error object instead.","solutions":["Open data.json and confirm the path resolves to a JSON array; fix the path in the directive to match the real location of the array.","If the array is legitimately optional, guard by ensuring data.json always provides an empty array `[]` for that key rather than omitting it.","Validate the data.json shape against the template's expected schema before persisting the artifact.","If you intended to iterate a non-array, restructure data.json to wrap the value(s) in an array."],"exampleFix":"// before — data.json: { \"data\": { \"title\": \"Hello\" } }, template: <li data-od-repeat=\"x in data.title\">{{x}}</li>\n// after — data.json: { \"data\": { \"items\": [\"Hello\"] } }, template: <li data-od-repeat=\"x in data.items\">{{x}}</li>","handlingStrategy":"validation","validationCode":"function readTemplatePath(dataJson: unknown, rawPath: string): unknown {\n  const segments = rawPath.split('.');\n  if (segments.shift() !== 'data') throw new Error(`path must start with data.`);\n  let cur: unknown = dataJson;\n  for (const seg of segments) cur = (cur as Record<string, unknown>)?.[seg];\n  return cur;\n}\n\n// Pre-check every repeat directive's source is an array before rendering.\nfor (const [, path] of templateHtml.matchAll(/\\bdata-od-repeat\\s*=\\s*\"[A-Za-z_][A-Za-z0-9_]*\\s+in\\s+(data[\\w.-]*)\"/gi)) {\n  if (!Array.isArray(readTemplatePath(dataJson, path))) {\n    throw new Error(`repeat source ${path} is not an array`);\n  }\n}","typeGuard":"function isArrayPath(dataJson: unknown, path: string): boolean {\n  const segments = path.split('.');\n  if (segments.shift() !== 'data') return false;\n  let cur: unknown = dataJson;\n  for (const seg of segments) cur = (cur as Record<string, unknown>)?.[seg];\n  return Array.isArray(cur);\n}","tryCatchPattern":null,"preventionTips":["Keep data.json and the template directive paths in sync via a shared schema.","Default optional arrays to `[]` in data.json rather than omitting them.","Validate the data.json shape against the template's expectations before persisting the artifact."],"tags":["live-artifacts","html-template","template-rendering","validation","data-binding"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}