windmill-labs/windmill · error · Error
Invalid note at index ${index}: must be an object
Error message
Invalid note at index ${index}: must be an object What it means
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.
Source
Thrown at frontend/src/lib/components/copilot/chat/flow/helperUtils.ts:169
if (rawNotes == null) {
return null
}
if (!Array.isArray(rawNotes)) {
throw new Error('Flow notes must be an array')
}
const seenIds = new Set<string>()
// Column and running y-cursor for auto-placed free notes so consecutive ones
// stack below each other by their actual heights instead of overlapping. A
// preserved note (explicit geometry) sitting in this column also advances the
// cursor, so a later auto-placed note doesn't land on top of it.
const AUTO_STACK_X = -(MIN_NOTE_WIDTH + 100)
const AUTO_STACK_GAP = 24
let autoStackY = 0
return rawNotes.map((note, index) => {
if (!note || typeof note !== 'object' || Array.isArray(note)) {
throw new Error(`Invalid note at index ${index}: must be an object`)
}
const n = note as Record<string, unknown>
if (typeof n.id !== 'string' || !n.id) {
throw new Error(`Invalid note at index ${index}: id must be a non-empty string`)
}
if (seenIds.has(n.id)) {
throw new Error(`Invalid note at index ${index}: duplicate note id "${n.id}"`)
}
seenIds.add(n.id)
if (typeof n.text !== 'string') {
throw new Error(`Invalid note at index ${index}: text must be a string`)
}
const type = n.type ?? 'free'
if (type !== 'free' && type !== 'group') {
throw new Error(`Invalid note at index ${index}: type must be "free" or "group"`)
}
if (n.color !== undefined && n.color !== null) {
if (typeof n.color !== 'string' || !ALLOWED_NOTE_COLORS.has(n.color)) {View on GitHub (pinned to e474e8803c)
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
Example fix
// before
notes: ['step 1 done', 'review']
// after
notes: [{ id: 'n1', text: 'step 1 done' }, { id: 'n2', text: 'review' }] Defensive patterns
Strategy: type-guard
Validate before calling
notes.forEach((n, i) => {
if (n === null || typeof n !== 'object' || Array.isArray(n)) throw new Error(`notes[${i}] must be an object`);
}); Type guard
function isNoteObject(n: unknown): n is Record<string, unknown> {
return n !== null && typeof n === 'object' && !Array.isArray(n);
} Try / catch
try {
validateFlowNotes(rawNotes, moduleIds);
} catch (e) {
if (e instanceof Error && e.message.includes('must be an object')) {
console.error('A notes array element is not an object — sanitize the list and retry');
} else throw e;
} Prevention
- 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
When it happens
Trigger: notes: [null], notes: ['a note'], notes: [['n1', 'text']] passed to patch_flow_json/set_flow_json.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Flow notes must be an array
- Invalid note at index ${index}: text must be a string
- Invalid note at index ${index}: id must be a non-empty strin
- Invalid note at index ${index}: duplicate note id "${n.id}"
- Invalid note at index ${index}: type must be "free" or "grou
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/29c4e4284a5f7942.
Report an issue: GitHub.