{"record":{"id":"76947a99a8a9fe5d","repo":"windmill-labs/windmill","slug":"unknown-top-level-flow-key-s-unknownkeys-join","errorCode":null,"errorMessage":"Unknown top-level flow key(s): ${unknownKeys.join(', ')}. Allowed keys: ${[...allowedKeys].join(', ')}","messagePattern":"Unknown top-level flow key\\(s\\): (.+?)\\. Allowed keys: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts","lineNumber":353,"sourceCode":"export function validateEditableFlowJson(\n\trawFlow: unknown,\n\tctx: FlowValidationContext = {}\n): EditableFlowJson {\n\tif (!rawFlow || typeof rawFlow !== 'object' || Array.isArray(rawFlow)) {\n\t\tthrow new Error('Flow JSON must be an object')\n\t}\n\n\tconst flow = rawFlow as Record<string, unknown>\n\n\t// Reject unknown top-level keys: silently dropping them would make patch\n\t// tools report success for edits that never land on the flow.\n\tconst allowedKeys = new Set<string>([\n\t\t...EDITABLE_FLOW_STRUCTURAL_KEYS,\n\t\t...FLOW_VALUE_SETTINGS_KEYS\n\t])\n\tconst unknownKeys = Object.keys(flow).filter((key) => !allowedKeys.has(key))\n\tif (unknownKeys.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Unknown top-level flow key(s): ${unknownKeys.join(', ')}. Allowed keys: ${[...allowedKeys].join(', ')}`\n\t\t)\n\t}\n\n\tconst settingsResult = flowValueSettingsSchema.safeParse(flow)\n\tif (!settingsResult.success) {\n\t\tconst issue = settingsResult.error.issues[0]\n\t\tconst path = issue?.path?.join('.') ?? 'settings'\n\t\tthrow new Error(`Invalid flow setting ${path}: ${issue?.message ?? 'unknown error'}`)\n\t}\n\tconst settings = pickFlowValueSettings(settingsResult.data)\n\n\tconst modules = validateFlowModules(flow.modules, ctx)\n\tconst schema = validateFlowSchema(flow.schema)\n\tconst preprocessorModule = validateOptionalFlowModule(\n\t\tflow.preprocessor_module,\n\t\t'preprocessor_module'\n\t)","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts#L335-L371","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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: {...}}."],"exampleFix":"// before\n{\"name\":\"myflow\",\"modules\":[...]}\n// after\n{\"modules\":[...]}","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['modules','schema','preprocessor_module','failure_module','groups','notes' /* + FLOW_VALUE_SETTINGS_KEYS */])\nconst unknown = Object.keys(flow).filter(k => !ALLOWED.has(k))\nif (unknown.length) console.warn('Strip unknown keys:', unknown)","typeGuard":null,"tryCatchPattern":"try {\n  const editable = validateEditableFlowJson(raw)\n} catch (e) {\n  if (String(e.message).startsWith('Unknown top-level flow key')) {\n    const keys = /key\\(s\\): ([^.]+)\\./.exec(String(e.message))?.[1]?.split(', ') ?? []\n    keys.forEach(k => delete raw[k])\n    return validateEditableFlowJson(raw)\n  }\n  throw e\n}","preventionTips":["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"],"tags":["validation","strict-mode","flow-json"],"backgroundTag":"unknown-field-rejected","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}