windmill-labs/windmill · error

Invalid group at index ${index}: end_id must be a non-empty

Error message

Invalid group at index ${index}: end_id must be a non-empty string

What it means

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.

Source

Thrown at frontend/src/lib/components/copilot/chat/flow/helperUtils.ts:105

): FlowGroup[] | null {
	if (rawGroups == null) {
		return null
	}

	if (!Array.isArray(rawGroups)) {
		throw new Error('Flow groups must be an array')
	}

	return rawGroups.map((group, index) => {
		if (!group || typeof group !== 'object' || Array.isArray(group)) {
			throw new Error(`Invalid group at index ${index}: must be an object`)
		}
		const g = group as Record<string, unknown>
		if (typeof g.start_id !== 'string' || !g.start_id) {
			throw new Error(`Invalid group at index ${index}: start_id must be a non-empty string`)
		}
		if (typeof g.end_id !== 'string' || !g.end_id) {
			throw new Error(`Invalid group at index ${index}: end_id must be a non-empty string`)
		}
		if (moduleIds) {
			if (!moduleIds.has(g.start_id)) {
				throw new Error(
					`Invalid group at index ${index}: start_id "${g.start_id}" does not match any flow module`
				)
			}
			if (!moduleIds.has(g.end_id)) {
				throw new Error(
					`Invalid group at index ${index}: end_id "${g.end_id}" does not match any flow module`
				)
			}
		}
		if (g.color !== undefined && g.color !== null) {
			if (typeof g.color !== 'string' || !ALLOWED_NOTE_COLORS.has(g.color)) {
				throw new Error(
					`Invalid group at index ${index}: color must be one of ${[...ALLOWED_NOTE_COLORS].join(', ')}`
				)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Set end_id to the string id of the module where the group should end
  2. For a single-module group, set end_id equal to start_id
  3. Remove the group if not needed

Example fix

// before
groups: [{ start_id: 'a', end_id: '' }]
// after
groups: [{ start_id: 'a', end_id: 'b' }]
Defensive patterns

Strategy: validation

Validate before calling

for (const [i, g] of (groups ?? []).entries()) {
  if (typeof g.end_id !== 'string' || !g.end_id)
    throw new Error(`groups[${i}].end_id must be a non-empty string`)
}

Type guard

function hasValidEndId(g) {
  return typeof g?.end_id === 'string' && g.end_id.length > 0
}

Try / catch

try {
  const groups = validateFlowGroups(raw, moduleIds)
} catch (e) {
  if (e.message.includes('end_id must be a non-empty string')) {
    const idx = Number(e.message.match(/index (\d+)/)?.[1])
    raw[idx].end_id = raw[idx].start_id // single-module group fallback
    return validateFlowGroups(raw, moduleIds)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling validateFlowGroups with a group object missing end_id, end_id: "", or a non-string end_id value.

Common situations: LLM truncating the group object; single-module group intended but written with only start_id and an empty end_id; placeholder not substituted.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/c5be87d02ef3cfca. Report an issue: GitHub.