{"record":{"id":"871aec4ee13dddf2","repo":"windmill-labs/windmill","slug":"invalid-group-at-index-index-start-id-must-be","errorCode":null,"errorMessage":"Invalid group at index ${index}: start_id must be a non-empty string","messagePattern":"Invalid group at index (.+?): start_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":102,"sourceCode":"export function validateFlowGroups(\n\trawGroups: unknown,\n\tmoduleIds?: Set<string>\n): 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)) {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/helperUtils.ts#L84-L120","documentation":"Each flow group must specify start_id as a non-empty string naming the module where the group (note box) begins. validateFlowGroups throws at the given index when start_id is missing, not a string, or an empty string.","triggerScenarios":"Calling validateFlowGroups with a group object lacking start_id, having start_id: \"\", or a non-string value like start_id: 3 or null.","commonSituations":"LLM emitting only an end_id; empty-string ids from template placeholders that were never substituted; numeric ids from another system.","solutions":["Set start_id to the string id of an existing flow module","Remove the group if it has no meaningful start","Ensure the id matches the module's id exactly (case-sensitive)"],"exampleFix":"// before\ngroups: [{ end_id: 'b' }]\n// after\ngroups: [{ start_id: 'a', end_id: 'b' }]","handlingStrategy":"validation","validationCode":"for (const [i, g] of (groups ?? []).entries()) {\n  if (typeof g.start_id !== 'string' || !g.start_id)\n    throw new Error(`groups[${i}].start_id must be a non-empty string`)\n}","typeGuard":"function hasValidStartId(g) {\n  return typeof g?.start_id === 'string' && g.start_id.length > 0\n}","tryCatchPattern":"try {\n  const groups = validateFlowGroups(raw, moduleIds)\n} catch (e) {\n  if (e.message.includes('start_id must be a non-empty string')) {\n    const idx = Number(e.message.match(/index (\\d+)/)?.[1])\n    raw[idx].start_id = raw[idx].end_id // single-module group fallback\n    return validateFlowGroups(raw, moduleIds)\n  }\n  throw e\n}","preventionTips":["Always set both start_id and end_id when defining a group","Substitute template placeholders before validating, never after","Copy module ids programmatically from the flow JSON rather than typing them"],"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"}