windmill-labs/windmill · error
Flow modules must be an array
Error message
Flow modules must be an array
What it means
validateFlowModules in editableFlowJson.ts is the shared validator for a flow's modules payload. Before schema-checking, it asserts the raw modules value is an array; anything else (object, string, null, undefined) throws this error. It exists to give callers an early, clear message instead of an opaque Zod type error.
Source
Thrown at frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts:217
(Object.keys(jsonSchema).length === 1 && jsonSchema.$schema)
) {
return null
}
const formatted = formatJsonSchemaForError(jsonSchema)
if (formatted && formatted !== 'unknown' && !formatted.startsWith('{')) return formatted
if (formatted && formatted.startsWith('{') && formatted !== '{ }') return formatted
} catch {
// Ignore errors from toJSONSchema
}
return null
}
export function validateFlowModules(
rawModules: unknown,
ctx: FlowValidationContext = {}
): FlowModule[] {
if (!Array.isArray(rawModules)) {
throw new Error('Flow modules must be an array')
}
const parsedModules = rawModules as FlowModule[]
const modulesSchema = ctx.modulesSchema ?? flowModulesSchema
const result = modulesSchema.safeParse(parsedModules)
if (!result.success) {
const errors = result.error.issues.slice(0, 5).map((e) => {
const path = e.path
const moduleIndex = typeof path[0] === 'number' ? path[0] : undefined
const moduleId = moduleIndex !== undefined ? parsedModules[moduleIndex]?.id : undefined
const fieldPath = path.slice(1).join('.')
let message = e.message
if (e.code === 'invalid_type') {
const targetSchema = getSchemaAtPath(
modulesSchema,
path as (string | number)[],
parsedModulesView on GitHub (pinned to e474e8803c)
Solutions
- Pass modules as a JSON array of module objects: [{ id, value, ... }, ...].
- If the payload is a JSON string, parse it with JSON.parse before validating.
- If modules are keyed by id, convert the object to an array with Object.values().
Example fix
// before
validateFlowJson({ modules: { a: { id: 'a' } } })
// after
validateFlowJson({ modules: [{ id: 'a', value: {...} }] }) Defensive patterns
Strategy: type-guard
Validate before calling
if (!Array.isArray(payload.modules)) throw new Error('modules must be an array of module objects') Type guard
function isModuleArray(v: unknown): v is FlowModule[] {
return Array.isArray(v) && v.every((m) => !!m && typeof m === 'object' && typeof (m as any).id === 'string')
} Try / catch
try {
validateFlowModules(rawModules)
} catch (e) {
if (e.message === 'Flow modules must be an array') {
rawModules = Array.isArray(rawModules) ? rawModules : Object.values(rawModules ?? {})
} else throw e
} Prevention
- Always serialize modules as a JSON array, never an id-keyed map
- Parse string payloads with JSON.parse before validating
- Shape payloads after the documented EditableFlowJson format
When it happens
Trigger: Calling validateFlowModules (directly or via flowTools 'modules' handling / validateEditableFlowJson) with a modules value that is not an array — e.g. an object keyed by module id, null, or a JSON string of the array.
Common situations: A model emits modules as a map ({"id": {...}}) instead of a list; the caller forgets to JSON.parse a string payload; a partially built object is passed where an array was expected.
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
- Invalid flow modules:\n${errors.join('\n')}
- result.substring(__RESULT_ERR_PREFIX.length)
- Invalid ${field}:\n${errors.join('\n')}
- Duplicate module IDs found in flow
- Special modules must be provided via preprocessor_module and
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/68bbf0681ddddfd5.
Report an issue: GitHub.