{"record":{"id":"c5be87d02ef3cfca","repo":"windmill-labs/windmill","slug":"invalid-group-at-index-index-end-id-must-be-a","errorCode":null,"errorMessage":"Invalid group at index ${index}: end_id must be a non-empty string","messagePattern":"Invalid group at index (.+?): end_id must be a non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/helperUtils.ts","lineNumber":105,"sourceCode":"): FlowGroup[] | null {\n\tif (rawGroups == null) {\n\t\treturn null\n\t}\n\n\tif (!Array.isArray(rawGroups)) {\n\t\tthrow new Error('Flow groups must be an array')\n\t}\n\n\treturn rawGroups.map((group, index) => {\n\t\tif (!group || typeof group !== 'object' || Array.isArray(group)) {\n\t\t\tthrow new Error(`Invalid group at index ${index}: must be an object`)\n\t\t}\n\t\tconst g = group as Record<string, unknown>\n\t\tif (typeof g.start_id !== 'string' || !g.start_id) {\n\t\t\tthrow new Error(`Invalid group at index ${index}: start_id must be a non-empty string`)\n\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)","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L87-L123","documentation":"Thrown by validateFlowGroups, a validation helper for LLM-supplied flow-group tool arguments, when the group object at `index` has an `end_id` that is missing, not a string, or empty. The flow-edit tools need each group to reference an existing end step id; malformed model-produced payloads are rejected here before the draft is touched.","triggerScenarios":"Calling validateFlowGroups with a group object missing end_id, end_id: \"\", or a non-string end_id value.","commonSituations":"LLM truncating the group object; single-module group intended but written with only start_id and an empty end_id; placeholder not substituted.","solutions":["Set end_id to the string id of the module where the group should end","For a single-module group, set end_id equal to start_id","Remove the group if not needed"],"exampleFix":"// before\ngroups: [{ start_id: 'a', end_id: '' }]\n// after\ngroups: [{ start_id: 'a', end_id: 'b' }]","handlingStrategy":"validation","validationCode":"for (const [i, g] of (groups ?? []).entries()) {\n  if (typeof g.end_id !== 'string' || !g.end_id)\n    throw new Error(`groups[${i}].end_id must be a non-empty string`)\n}","typeGuard":"function hasValidEndId(g) {\n  return typeof g?.end_id === 'string' && g.end_id.length > 0\n}","tryCatchPattern":"try {\n  const groups = validateFlowGroups(raw, moduleIds)\n} catch (e) {\n  if (e.message.includes('end_id must be a non-empty string')) {\n    const idx = Number(e.message.match(/index (\\d+)/)?.[1])\n    raw[idx].end_id = raw[idx].start_id // single-module group fallback\n    return validateFlowGroups(raw, moduleIds)\n  }\n  throw e\n}","preventionTips":["Include end_id in every group object, even single-module groups","Let the LLM schema require end_id so omission fails at generation time","Validate groups immediately after construction to catch missing fields early"],"tags":["validation","flow-groups","missing-field"],"backgroundTag":"schema-validation-failed","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"}