{"record":{"id":"dc5ab7baefcf5f2e","repo":"windmill-labs/windmill","slug":"invalid-note-at-index-index-contained-node-ids-dc5ab7","errorCode":null,"errorMessage":"Invalid note at index ${index}: contained_node_ids \"${id}\" does not match any flow module","messagePattern":"Invalid note at index (.+?): contained_node_ids \"(.+?)\" does not match any flow module","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/helperUtils.ts","lineNumber":231,"sourceCode":"\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid note at index ${index}: size must be an object with numeric width and height`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (type === 'group' && n.contained_node_ids !== undefined) {\n\t\t\tif (\n\t\t\t\t!Array.isArray(n.contained_node_ids) ||\n\t\t\t\tn.contained_node_ids.some((id) => typeof id !== 'string')\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid note at index ${index}: contained_node_ids must be an array of strings`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (moduleIds) {\n\t\t\t\tfor (const id of n.contained_node_ids as string[]) {\n\t\t\t\t\tif (!moduleIds.has(id)) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Invalid note at index ${index}: contained_node_ids \"${id}\" does not match any flow module`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst normalized = {\n\t\t\t...(n as FlowNote),\n\t\t\ttype,\n\t\t\t// Preserve a provided color; only seed the default when omitted.\n\t\t\tcolor: typeof n.color === 'string' ? n.color : DEFAULT_NOTE_COLOR\n\t\t} as FlowNote\n\n\t\t// Free notes need explicit geometry to be draggable/resizable. Size first\n\t\t// (from text, so tall notes get a tall box), then place any note missing a\n\t\t// position to the left of the flow column, stacking auto-placed notes by\n\t\t// their real heights so several generated notes don't overlap. Group notes\n\t\t// derive their layout from contained nodes, so they are left alone.","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L213-L249","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncontained_node_ids: ['step_1'] // not a real module id\n// after\ncontained_node_ids: ['9f2c...'] // id taken from the flow's actual module list","handlingStrategy":"validation","validationCode":"const known = new Set(modules.map((m) => m.id))\nfor (const n of notes) {\n  for (const id of n.contained_node_ids ?? []) {\n    if (!known.has(id)) throw new Error(`unknown module id: ${id}`)\n  }\n}","typeGuard":"function referencesKnownModules(ids: unknown, moduleIds: Set<string>): ids is string[] {\n  return Array.isArray(ids) && ids.every((id) => typeof id === 'string' && moduleIds.has(id))\n}","tryCatchPattern":"try {\n  validateFlowNotes(notes, moduleIds)\n} catch (e) {\n  if (e.message.includes('does not match any flow module')) {\n    console.warn('filtering unknown group ids');\n    notes = notes.map((n) => ({ ...n, contained_node_ids: (n.contained_node_ids ?? []).filter((id) => moduleIds.has(id)) }))\n  }\n}","preventionTips":["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"],"tags":["validation","referential-integrity","flow-notes","ai-tools"],"backgroundTag":"unresolved-reference","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"}