windmill-labs/windmill · error

Invalid group at index ${index}: must be an object

Error message

Invalid group at index ${index}: must be an object

What it means

Each entry of the flow groups array must be a plain object describing a note box (start_id, end_id, optional summary/color). validateFlowGroups rejects null, non-object, or array elements at the given index so downstream code can safely read group fields.

Source

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

	rawScriptModule.value.content = code
	return rawScriptModule
}

export function validateFlowGroups(
	rawGroups: unknown,
	moduleIds?: Set<string>
): 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`
				)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Replace the invalid element with a group object: { start_id: '<module id>', end_id: '<module id>' }
  2. Remove the null/primitive entry from the array
  3. If ids-only was intended, restructure as objects with start_id/end_id

Example fix

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

Strategy: type-guard

Validate before calling

const bad = (groups ?? []).findIndex(g => g == null || typeof g !== 'object' || Array.isArray(g))
if (bad !== -1) throw new Error(`groups[${bad}] must be an object`)

Type guard

function isFlowGroup(g) {
  return typeof g === 'object' && g !== null && !Array.isArray(g)
}

Try / catch

try {
  const groups = validateFlowGroups(raw, moduleIds)
} catch (e) {
  if (e.message.includes('must be an object')) {
    const idx = Number(e.message.match(/index (\d+)/)?.[1])
    sanitized.splice(idx, 1) // drop the bad entry and retry
    return validateFlowGroups(sanitized, moduleIds)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling validateFlowGroups with an array containing null, a primitive (e.g. 'group1', 42), or a nested array at that index — e.g. groups: [null] or groups: [['a','b']].

Common situations: LLM emitting a list of id pairs instead of group objects; JSON with empty slots; mapping over module ids directly and passing the result as groups.

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/7414a67ad4521f1d. Report an issue: GitHub.