windmill-labs/windmill · error

Special modules must be provided via preprocessor_module and

Error message

Special modules must be provided via preprocessor_module and failure_module, not inside modules

What it means

When setting a whole flow via the set-flow tool, modules destined for the flow's preprocessor or failure handler must be passed in the dedicated preprocessor_module / failure_module fields, not as entries inside the modules array. After validation, the tool scans all module IDs for the reserved special IDs (preprocessor/failure) and throws this error if any appear inline in modules.

Source

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

				| undefined
			parsedGroups = parseOptionalJsonArg(groups, 'groups') as FlowGroup[] | null | undefined
			parsedNotes = parseOptionalJsonArg(notes, 'notes') as FlowNote[] | null | undefined
			if (parsedModules === null) {
				parsedModules = undefined
			}
			if (parsedSchema === null) {
				parsedSchema = undefined
			}

			const aiProviderWarnings: string[] = []
			if (parsedModules !== undefined) {
				const aiProviders = await getAiAgentProviderCatalogFor(workspace, parsedModules)
				parsedModules = validateFlowModules(parsedModules, { aiProviders, aiProviderWarnings })
				const reservedIds = collectAllFlowModuleIdsFromModules(parsedModules).filter(
					(id) => id === SPECIAL_MODULE_IDS.PREPROCESSOR || id === SPECIAL_MODULE_IDS.FAILURE
				)
				if (reservedIds.length > 0) {
					throw new Error(
						'Special modules must be provided via preprocessor_module and failure_module, not inside modules'
					)
				}
			}
			if (parsedSchema !== undefined) {
				parsedSchema = validateFlowSchema(parsedSchema)
			}

			parsedPreprocessorModule = validateSpecialFlowModule(
				parsedPreprocessorModule,
				'preprocessor_module'
			)
			parsedFailureModule = validateSpecialFlowModule(parsedFailureModule, 'failure_module')

			if (parsedGroups !== undefined || parsedNotes !== undefined) {
				const effectiveModules =
					parsedModules ?? helpers.getFlowAndSelectedId().flow.value.modules ?? []
				const moduleIdsForGroups = new Set(collectAllFlowModuleIdsFromModules(effectiveModules))

View on GitHub (pinned to e474e8803c)

Solutions

  1. Remove the preprocessor/failure module from the modules array and pass it via the preprocessor_module or failure_module field.
  2. Keep the module's content identical — only its location changes from modules[] to the dedicated field.
  3. Regenerate the payload from the editable flow JSON view, which already separates special modules.

Example fix

// before
setFlow({ modules: [...other, { id: 'preprocessor', value: {...} }] })
// after
setFlow({ modules: [...other], preprocessor_module: { id: 'preprocessor', value: {...} } })
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = ['preprocessor', 'failure'] // SPECIAL_MODULE_IDS
const inline = modules.filter((m) => RESERVED.includes(m.id))
if (inline.length) throw new Error(`Move ${inline.map((m) => m.id).join(',')} out of modules into preprocessor_module/failure_module`)

Type guard

function usesReservedIdsInline(modules: FlowModule[]): boolean {
  return modules.some((m) => m.id === 'preprocessor' || m.id === 'failure')
}

Try / catch

try {
  await setFlow(payload)
} catch (e) {
  if (e.message.includes('Special modules must be provided via')) {
    const fixed = liftSpecialModules(payload) // move matching modules to dedicated fields
    await setFlow(fixed)
  } else throw e
}

Prevention

When it happens

Trigger: Calling set_flow (flowTools setFlow path) with a modules array containing a module whose id equals SPECIAL_MODULE_IDS.PREPROCESSOR or SPECIAL_MODULE_IDS.FAILURE, instead of supplying it via the preprocessor_module/failure_module arguments.

Common situations: The AI read a full backend flow payload (which embeds preprocessor/failure inside the modules list at the top level of the flow value) and echoed it back verbatim; hand-written flow JSON copied from an export that keeps special modules inline.

Related errors


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