windmill-labs/windmill · error · Error
Invalid note at index ${index}: contained_node_ids "${id}" d
Error message
Invalid note at index ${index}: contained_node_ids "${id}" does not match any flow module What it means
After the array-of-strings check, validateFlowNotes cross-references each id in a group note's contained_node_ids against the set of module ids actually present in the flow (when a moduleIds set was supplied). This error is thrown when an id refers to a module that does not exist in the flow, preventing dangling group memberships.
Source
Thrown at frontend/src/lib/components/copilot/chat/flow/helperUtils.ts:231
) {
throw new Error(
`Invalid note at index ${index}: size must be an object with numeric width and height`
)
}
}
if (type === 'group' && n.contained_node_ids !== undefined) {
if (
!Array.isArray(n.contained_node_ids) ||
n.contained_node_ids.some((id) => typeof id !== 'string')
) {
throw new Error(
`Invalid note at index ${index}: contained_node_ids must be an array of strings`
)
}
if (moduleIds) {
for (const id of n.contained_node_ids as string[]) {
if (!moduleIds.has(id)) {
throw new Error(
`Invalid note at index ${index}: contained_node_ids "${id}" does not match any flow module`
)
}
}
}
}
const normalized = {
...(n as FlowNote),
type,
// Preserve a provided color; only seed the default when omitted.
color: typeof n.color === 'string' ? n.color : DEFAULT_NOTE_COLOR
} as FlowNote
// Free notes need explicit geometry to be draggable/resizable. Size first
// (from text, so tall notes get a tall box), then place any note missing a
// position to the left of the flow column, stacking auto-placed notes by
// their real heights so several generated notes don't overlap. Group notes
// derive their layout from contained nodes, so they are left alone.View on GitHub (pinned to e474e8803c)
Solutions
- Use the exact module ids returned by the module-listing/read tool output, not invented names.
- Re-read the current flow modules and rebuild contained_node_ids from them.
- Remove the offending id from contained_node_ids, or drop the field to place an unanchored group.
- If creating modules and notes in one call, order the calls so modules exist before the group note references them.
Example fix
// before contained_node_ids: ['step_1'] // not a real module id // after contained_node_ids: ['9f2c...'] // id taken from the flow's actual module list
Defensive patterns
Strategy: validation
Validate before calling
const known = new Set(modules.map((m) => m.id))
for (const n of notes) {
for (const id of n.contained_node_ids ?? []) {
if (!known.has(id)) throw new Error(`unknown module id: ${id}`)
}
} Type guard
function referencesKnownModules(ids: unknown, moduleIds: Set<string>): ids is string[] {
return Array.isArray(ids) && ids.every((id) => typeof id === 'string' && moduleIds.has(id))
} Try / catch
try {
validateFlowNotes(notes, moduleIds)
} catch (e) {
if (e.message.includes('does not match any flow module')) {
console.warn('filtering unknown group ids');
notes = notes.map((n) => ({ ...n, contained_node_ids: (n.contained_node_ids ?? []).filter((id) => moduleIds.has(id)) }))
}
} Prevention
- Copy module ids verbatim from the latest module-list tool output
- Re-read the flow before emitting notes if earlier steps may have changed it
- Never let the model invent module ids; supply the real ones in the prompt context
When it happens
Trigger: A group note lists contained_node_ids like ["step_1"] but the flow's modules are keyed differently (e.g. "a1b2c3" uuids), the module was renamed/removed before the note call, or the LLM hallucinated an id.
Common situations: Model invents human-readable ids instead of using real module ids from earlier tool output; the flow was regenerated between the module listing and the notes call; stale ids copied from a previous flow version.
Related errors
- Invalid group at index ${index}: start_id "${g.start_id}" do
- Invalid group at index ${index}: end_id "${g.end_id}" does n
- Flow notes must be an array
- Invalid note at index ${index}: must be an object
- Invalid note at index ${index}: id must be a non-empty strin
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/dc5ab7baefcf5f2e.
Report an issue: GitHub.