{"record":{"id":"d38f6dd33f2e3599","repo":"windmill-labs/windmill","slug":"invalid-group-at-index-index-start-id-g-sta","errorCode":null,"errorMessage":"Invalid group at index ${index}: start_id \"${g.start_id}\" does not match any flow module","messagePattern":"Invalid group at index (.+?): start_id \"(.+?)\" 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":121,"sourceCode":"\t\t}\n\t\tif (typeof g.end_id !== 'string' || !g.end_id) {\n\t\t\tthrow new Error(`Invalid group at index ${index}: end_id must be a non-empty string`)\n\t\t}\n\t\tif (moduleIds) {\n\t\t\tif (!moduleIds.has(g.start_id)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid group at index ${index}: start_id \"${g.start_id}\" does not match any flow module`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (!moduleIds.has(g.end_id)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid group at index ${index}: end_id \"${g.end_id}\" does not match any flow module`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tif (g.color !== undefined && g.color !== null) {\n\t\t\tif (typeof g.color !== 'string' || !ALLOWED_NOTE_COLORS.has(g.color)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid group 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\treturn g as unknown as FlowGroup\n\t})\n}\n\n/**\n * Validate the optional array of sticky notes the agent attached to the flow.\n * Notes are editor-only annotations and do not affect execution.\n *\n * `free` notes are the supported kind (standalone canvas annotations). The\n * `group` note type is deprecated for creation — the chat prompt steers the\n * agent toward `groups` instead — but it is still ACCEPTED here so flows that\n * already contain group notes round-trip cleanly through `patch_flow_json` /\n * `set_flow_json` rather than being rejected. When `moduleIds` is provided,\n * every `contained_node_ids` entry of a `group` note must reference an existing","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L103-L139","documentation":"validateFlowGroups validates the `groups` array an AI agent passes to patch_flow_json/set_flow_json. Each group's start_id and end_id must reference an existing flow module; when a moduleIds set is supplied and start_id is not found in it, this error is thrown. It exists to prevent groups that visually dangle on the canvas because they point at modules that don't exist.","triggerScenarios":"Calling updateFlowJson/patch_flow_json with groups: [{start_id: 'mod_x', end_id: 'mod_y'}] where 'mod_x' is not a module id in the current flow (typo, hallucinated id by the LLM, or a module deleted earlier in the same edit).","commonSituations":"An LLM agent invents plausible-looking module ids instead of copying them from the flow it was given; the agent references a module from a previous flow revision that was renamed or removed; the caller forgets to include newly-added module ids in the moduleIds set.","solutions":["Copy module ids verbatim from the flow value the tools returned (each module's `id` field), never invent them","Re-fetch the current flow JSON to see which module ids actually exist before setting groups","If the module was intended to be new, add the module itself in the same update and use its generated id","Drop or fix the offending group entry at the reported index"],"exampleFix":"// before\ngroups: [{ start_id: 'step_1', end_id: 'step_2' }]\n// after — use real ids from flow.value.modules\ngroups: [{ start_id: flow.value.modules[0].id, end_id: flow.value.modules[1].id }]","handlingStrategy":"validation","validationCode":"const moduleIds = new Set<string>(); forEachFlowModule(flow.value, (m) => { moduleIds.add(m.id); });\nfor (const [i, g] of (groups ?? []).entries()) {\n  if (!moduleIds.has(g.start_id)) throw new Error(`groups[${i}].start_id \"${g.start_id}\" is not a flow module id`);\n}","typeGuard":"function isValidGroupId(id: unknown, moduleIds: Set<string>): id is string {\n  return typeof id === 'string' && moduleIds.has(id);\n}","tryCatchPattern":"try {\n  const validated = validateFlowGroups(rawGroups, moduleIds);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('does not match any flow module')) {\n    // surface index + offending id back to the agent so it can re-read the flow\n  } else throw e;\n}","preventionTips":["Always build groups from the module ids present in the latest flow value you received from the tool","Copy ids verbatim; never abbreviate or re-generate them","If you added modules in the same update, use the exact ids you assigned them"],"tags":["validation","flow-groups","module-id","referential-integrity"],"backgroundTag":"unknown-referenced-id","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"}