{"record":{"id":"29c4e4284a5f7942","repo":"windmill-labs/windmill","slug":"invalid-note-at-index-index-must-be-an-object","errorCode":null,"errorMessage":"Invalid note at index ${index}: must be an object","messagePattern":"Invalid note at index (.+?): must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/helperUtils.ts","lineNumber":169,"sourceCode":"\tif (rawNotes == null) {\n\t\treturn null\n\t}\n\n\tif (!Array.isArray(rawNotes)) {\n\t\tthrow new Error('Flow notes must be an array')\n\t}\n\n\tconst seenIds = new Set<string>()\n\t// Column and running y-cursor for auto-placed free notes so consecutive ones\n\t// stack below each other by their actual heights instead of overlapping. A\n\t// preserved note (explicit geometry) sitting in this column also advances the\n\t// cursor, so a later auto-placed note doesn't land on top of it.\n\tconst AUTO_STACK_X = -(MIN_NOTE_WIDTH + 100)\n\tconst AUTO_STACK_GAP = 24\n\tlet autoStackY = 0\n\treturn rawNotes.map((note, index) => {\n\t\tif (!note || typeof note !== 'object' || Array.isArray(note)) {\n\t\t\tthrow new Error(`Invalid note at index ${index}: must be an object`)\n\t\t}\n\t\tconst n = note as Record<string, unknown>\n\t\tif (typeof n.id !== 'string' || !n.id) {\n\t\t\tthrow new Error(`Invalid note at index ${index}: id must be a non-empty string`)\n\t\t}\n\t\tif (seenIds.has(n.id)) {\n\t\t\tthrow new Error(`Invalid note at index ${index}: duplicate note id \"${n.id}\"`)\n\t\t}\n\t\tseenIds.add(n.id)\n\t\tif (typeof n.text !== 'string') {\n\t\t\tthrow new Error(`Invalid note at index ${index}: text must be a string`)\n\t\t}\n\t\tconst type = n.type ?? 'free'\n\t\tif (type !== 'free' && type !== 'group') {\n\t\t\tthrow new Error(`Invalid note at index ${index}: type must be \"free\" or \"group\"`)\n\t\t}\n\t\tif (n.color !== undefined && n.color !== null) {\n\t\t\tif (typeof n.color !== 'string' || !ALLOWED_NOTE_COLORS.has(n.color)) {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L151-L187","documentation":"Each entry of the notes array must be a non-null, non-array object. validateFlowNotes throws this error when an element is null, a primitive, or an array, because FlowNote requires named fields (id, text, etc.) that only an object can carry.","triggerScenarios":"notes: [null], notes: ['a note'], notes: [['n1', 'text']] passed to patch_flow_json/set_flow_json.","commonSituations":"An LLM agent serializes notes as CSV-like arrays; a null placeholder slipped into the list; a stringified note was double-encoded and ended up a string element.","solutions":["Make each array element a plain object with at least id and text fields","Remove null/empty placeholder entries from the array","If data is tabular, convert each row to an object before passing"],"exampleFix":"// before\nnotes: ['step 1 done', 'review']\n// after\nnotes: [{ id: 'n1', text: 'step 1 done' }, { id: 'n2', text: 'review' }]","handlingStrategy":"type-guard","validationCode":"notes.forEach((n, i) => {\n  if (n === null || typeof n !== 'object' || Array.isArray(n)) throw new Error(`notes[${i}] must be an object`);\n});","typeGuard":"function isNoteObject(n: unknown): n is Record<string, unknown> {\n  return n !== null && typeof n === 'object' && !Array.isArray(n);\n}","tryCatchPattern":"try {\n  validateFlowNotes(rawNotes, moduleIds);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must be an object')) {\n    console.error('A notes array element is not an object — sanitize the list and retry');\n  } else throw e;\n}","preventionTips":["Filter out null/undefined placeholders before submitting notes","Build notes as objects with named fields, never positional arrays or raw strings","Run a quick shape check on LLM-generated JSON before passing it through"],"tags":["validation","flow-notes","type-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}