{"record":{"id":"49b49c2e9fb0c40a","repo":"windmill-labs/windmill","slug":"invalid-note-at-index-index-id-must-be-a-non-e","errorCode":null,"errorMessage":"Invalid note at index ${index}: id must be a non-empty string","messagePattern":"Invalid note at index (.+?): id must be a non-empty string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/helperUtils.ts","lineNumber":173,"sourceCode":"\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)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid note at index ${index}: color must be one of ${[...ALLOWED_NOTE_COLORS].join(', ')}`\n\t\t\t\t)\n\t\t\t}","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L155-L191","documentation":"Thrown by validateFlowNotes when the flow note at `index` lacks a non-empty string `id`. Notes are keyed into the flow draft by id and auto-placement needs a stable identity, so LLM-supplied note arrays without ids are rejected by this guard before the draft is written.","triggerScenarios":"notes: [{ text: 'hi' }], notes: [{ id: '', text: 'hi' }], or notes: [{ id: 42, text: 'hi' }] passed to patch_flow_json/set_flow_json.","commonSituations":"An LLM agent omits ids for brevity; a caller assumes ids are auto-generated (they are not, unlike UI-created notes); numeric database keys were passed instead of strings.","solutions":["Add a unique non-empty string id to every note, e.g. id: 'note-1'","Convert numeric ids to strings before passing","Generate ids yourself (e.g. crypto.randomUUID()) when creating notes programmatically"],"exampleFix":"// before\nnotes: [{ text: 'Review auth flow' }]\n// after\nnotes: [{ id: 'note-1', text: 'Review auth flow' }]","handlingStrategy":"validation","validationCode":"notes.forEach((n, i) => {\n  if (typeof n.id !== 'string' || !n.id) throw new Error(`notes[${i}].id must be a non-empty string`);\n});","typeGuard":"function hasNoteId(n: unknown): n is { id: string } & Record<string, unknown> {\n  return typeof (n as any)?.id === 'string' && (n as any).id.length > 0;\n}","tryCatchPattern":"try {\n  validateFlowNotes(rawNotes, moduleIds);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('id must be a non-empty string')) {\n    // regenerate ids and retry\n  } else throw e;\n}","preventionTips":["Assign an explicit unique string id to every note at creation time","Use randomUUID() when generating ids programmatically","Never rely on the validator to auto-fill note ids — it does not"],"tags":["validation","flow-notes","required-field","identifier"],"backgroundTag":"missing-required-argument","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"}