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
- Remove the preprocessor/failure module from the modules array and pass it via the preprocessor_module or failure_module field.
- Keep the module's content identical — only its location changes from modules[] to the dedicated field.
- 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
- Never echo raw backend flow payloads; use the editable flow JSON shape
- Treat 'preprocessor' and 'failure' IDs as reserved everywhere in payloads
- Build payloads from buildEditableFlowJson, which separates special modules
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
- Duplicate module IDs found in flow
- Column ${column.field} is not nullable and has no default va
- BigQuery requires a dataset (schema) name
- AI response was empty
- AI response contained empty code block
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/b6109b72f13bec3d.
Report an issue: GitHub.