windmill-labs/windmill · error
Unknown top-level flow key(s): ${unknownKeys.join(', ')}. Al
Error message
Unknown top-level flow key(s): ${unknownKeys.join(', ')}. Allowed keys: ${[...allowedKeys].join(', ')} What it means
After confirming the flow is an object, validateEditableFlowJson rejects any top-level key outside the allowed set (EDITABLE_FLOW_STRUCTURAL_KEYS plus FLOW_VALUE_SETTINGS_KEYS). Unknown keys are not silently dropped, because a patch tool would then report success for an edit that never landed on the flow.
Source
Thrown at frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts:353
export function validateEditableFlowJson(
rawFlow: unknown,
ctx: FlowValidationContext = {}
): EditableFlowJson {
if (!rawFlow || typeof rawFlow !== 'object' || Array.isArray(rawFlow)) {
throw new Error('Flow JSON must be an object')
}
const flow = rawFlow as Record<string, unknown>
// Reject unknown top-level keys: silently dropping them would make patch
// tools report success for edits that never land on the flow.
const allowedKeys = new Set<string>([
...EDITABLE_FLOW_STRUCTURAL_KEYS,
...FLOW_VALUE_SETTINGS_KEYS
])
const unknownKeys = Object.keys(flow).filter((key) => !allowedKeys.has(key))
if (unknownKeys.length > 0) {
throw new Error(
`Unknown top-level flow key(s): ${unknownKeys.join(', ')}. Allowed keys: ${[...allowedKeys].join(', ')}`
)
}
const settingsResult = flowValueSettingsSchema.safeParse(flow)
if (!settingsResult.success) {
const issue = settingsResult.error.issues[0]
const path = issue?.path?.join('.') ?? 'settings'
throw new Error(`Invalid flow setting ${path}: ${issue?.message ?? 'unknown error'}`)
}
const settings = pickFlowValueSettings(settingsResult.data)
const modules = validateFlowModules(flow.modules, ctx)
const schema = validateFlowSchema(flow.schema)
const preprocessorModule = validateOptionalFlowModule(
flow.preprocessor_module,
'preprocessor_module'
)View on GitHub (pinned to e474e8803c)
Solutions
- Remove all listed unknown keys and keep only allowed ones (modules, schema, preprocessor_module, failure_module, groups, notes, and the flow-value settings keys).
- Move flow metadata (name, path, description) to the appropriate deploy/API call, not the editable flow JSON.
- Unwrap any wrapper object so the flow object itself is passed, not {flow: {...}}.
Example fix
// before
{"name":"myflow","modules":[...]}
// after
{"modules":[...]} Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = new Set(['modules','schema','preprocessor_module','failure_module','groups','notes' /* + FLOW_VALUE_SETTINGS_KEYS */])
const unknown = Object.keys(flow).filter(k => !ALLOWED.has(k))
if (unknown.length) console.warn('Strip unknown keys:', unknown) Try / catch
try {
const editable = validateEditableFlowJson(raw)
} catch (e) {
if (String(e.message).startsWith('Unknown top-level flow key')) {
const keys = /key\(s\): ([^.]+)\./.exec(String(e.message))?.[1]?.split(', ') ?? []
keys.forEach(k => delete raw[k])
return validateEditableFlowJson(raw)
}
throw e
} Prevention
- Strip server-only metadata (name, path, version, tags) before writing editable flow JSON
- Only round-trip keys that buildEditableFlowJson produced
- Never wrap the flow in an extra object level
When it happens
Trigger: Calling flowTools parsedFlow/patch tools with top-level keys like "name", "description", "path", "tags", or nested wrapper objects (e.g. {"flow":{...}}) that are not part of the editable representation.
Common situations: An LLM echoes back extra fields from a GET response (summary, path, version); a user pastes a full flow export including metadata; the payload is wrapped in an extra object level.
Related errors
- AI agent modules ${providerless.map((id) => `"${id}"`).join(
- Flow JSON must be an object
- result.substring(__RESULT_ERR_PREFIX.length)
- Invalid migration name '${name}': use only letters, digits,
- Unknown workspace dependencies file format: ${path}. Valid f
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/76947a99a8a9fe5d.
Report an issue: GitHub.