{"record":{"id":"bfe48738dcf9e6ff","repo":"windmill-labs/windmill","slug":"invalid-note-at-index-index-text-must-be-a-str","errorCode":null,"errorMessage":"Invalid note at index ${index}: text must be a string","messagePattern":"Invalid note at index (.+?): text must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/helperUtils.ts","lineNumber":180,"sourceCode":"\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}\n\t\t}\n\t\tif (n.position !== undefined && n.position !== null) {\n\t\t\tconst p = n.position as Record<string, unknown>\n\t\t\tif (\n\t\t\t\ttypeof p !== 'object' ||\n\t\t\t\tArray.isArray(n.position) ||\n\t\t\t\ttypeof p.x !== 'number' ||","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L162-L198","documentation":"A note's text field holds the sticky-note content and must be a string (empty string allowed). validateFlowNotes throws this error when text is missing or of another type, since the renderer and editor operate on plain string content.","triggerScenarios":"notes: [{ id: 'n1' }] (text missing), notes: [{ id: 'n1', text: 123 }], or notes: [{ id: 'n1', text: { body: 'hi' } }] passed to patch_flow_json/set_flow_json.","commonSituations":"An LLM agent puts rich structured content (markdown object, array of lines) into text; a caller passes null for empty notes instead of ''; numeric or boolean content was passed unconverted.","solutions":["Provide text as a plain string on every note, even if empty ('')","Stringify structured content (join lines with \\n, serialize objects) before passing","Convert numbers/booleans with String(...) if the content is scalar"],"exampleFix":"// before\nnotes: [{ id: 'n1', text: ['line1', 'line2'] }]\n// after\nnotes: [{ id: 'n1', text: 'line1\\nline2' }]","handlingStrategy":"validation","validationCode":"notes.forEach((n, i) => {\n  if (typeof n.text !== 'string') throw new Error(`notes[${i}].text must be a string`);\n});","typeGuard":"function hasNoteText(n: unknown): n is { text: string } & Record<string, unknown> {\n  return typeof (n as any)?.text === 'string';\n}","tryCatchPattern":"try {\n  validateFlowNotes(rawNotes, moduleIds);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('text must be a string')) {\n    // stringify offending text fields and retry\n  } else throw e;\n}","preventionTips":["Always set text as a plain string, using \\n for multi-line content","Convert structured content to a string before building the note","Use '' rather than null for intentionally empty notes"],"tags":["validation","flow-notes","type-mismatch","required-field"],"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"}