windmill-labs/windmill · error

Invalid ${field}: id must be "${expectedId}"

Error message

Invalid ${field}: id must be "${expectedId}"

What it means

After schema validation succeeds, validateSpecialFlowModule additionally requires the module's id to equal the fixed expected id for that special field (specialFlowModuleFields[field]). A mismatch throws 'Invalid <field>: id must be "<expectedId>"'.

Source

Thrown at frontend/src/lib/components/copilot/chat/flow/core.ts:274

	field: SpecialFlowModuleField
): FlowModule | null | undefined {
	if (module === undefined || module === null) {
		return module
	}

	const result = flowModuleSchema.safeParse(module)
	if (!result.success) {
		const errors = result.error.issues.slice(0, 5).map((issue) => {
			const path = issue.path.length > 0 ? issue.path.join('.') : field
			return `${path}: ${issue.message}`
		})
		throw new Error(`Invalid ${field}:\n${errors.join('\n')}`)
	}

	const parsedModule = result.data
	const expectedId = specialFlowModuleFields[field]
	if (parsedModule.id !== expectedId) {
		throw new Error(`Invalid ${field}: id must be "${expectedId}"`)
	}

	if (parsedModule.value.type !== 'rawscript' && parsedModule.value.type !== 'script') {
		throw new Error(`Invalid ${field}: only "rawscript" and "script" modules are supported`)
	}

	return parsedModule
}

const patchFlowJsonSchema = z.object({
	old_string: z.string().min(1).describe('Exact text to find in the current compact flow JSON'),
	new_string: z.string().describe('Replacement JSON text'),
	replace_all: z
		.boolean()
		.optional()
		.default(false)
		.describe(
			'When true, replace every exact match. When false, the search text must match exactly once.'

View on GitHub (pinned to e474e8803c)

Solutions

  1. Set the module's id to the exact expected value reported in the error message.
  2. Ensure you are supplying the module to the correct special field (each field has its own expected id).
  3. Regenerate the flow JSON with the instruction that special modules use fixed sentinel ids.

Example fix

// before
{ "id": "approve_step", "value": ... }
// after
{ "id": "approval", "value": ... }
Defensive patterns

Strategy: validation

Validate before calling

const expectedId = specialFlowModuleFields[field]
if (module.id !== expectedId) {
  module = { ...module, id: expectedId }
}

Type guard

function hasExpectedSpecialId(field: SpecialFlowModuleField, m: { id: string }): boolean {
  return m.id === specialFlowModuleFields[field]
}

Try / catch

try {
  await tool.updateSpecialModule(field, module)
} catch (e) {
  const m = e.message.match(/id must be "(.+)"/)
  if (m) return tool.updateSpecialModule(field, { ...module, id: m[1] })
  throw e
}

Prevention

When it happens

Trigger: The parsed module is structurally valid but parsedModule.id !== specialFlowModuleFields[field] — e.g. an id like 'my_step' or a duplicate/renamed id where the fixed sentinel id is required.

Common situations: Model invented a custom id for the special module; the module was copied from another flow and its id was edited; the caller passed the module into the wrong special field.

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/556654fd0bbb5717. Report an issue: GitHub.