windmill-labs/windmill · error · Error
Invalid JSON for ${field}: ${message}${hint}
Error message
Invalid JSON for ${field}: ${message}${hint} What it means
parseOptionalJsonArg parses tool arguments that may arrive as a JSON string. When JSON.parse fails it wraps the parse error into `Invalid JSON for <field>: <message>`, and for fields that carry flow code (FLOW_CODE_BEARING_FIELDS, e.g. rawscript content) it appends a hint: multi-line code or quotes inside the JSON string break escaping, and large bodies should instead be created empty and filled via set_flow_module_code.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:528
})
// modules/preprocessor_module/failure_module can carry rawscript `content`, whose
// quotes and newlines are the usual reason the JSON string fails to parse.
const FLOW_CODE_BEARING_FIELDS = new Set(['modules', 'preprocessor_module', 'failure_module'])
function parseOptionalJsonArg(value: unknown, field: string): unknown {
if (value === undefined || value === null) {
return value
}
try {
return typeof value === 'string' ? JSON.parse(value) : value
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
const hint = FLOW_CODE_BEARING_FIELDS.has(field)
? ' A rawscript "content" string with multi-line code or quotes is the usual cause. Instead of inlining large code, create the rawscript module with empty content ("") and fill its body with set_flow_module_code afterwards.'
: ''
throw new Error(`Invalid JSON for ${field}: ${message}${hint}`)
}
}
/**
* Rawscript bodies are fragile to embed inside the `modules` JSON string: the
* code's quotes and newlines have to survive three levels of escaping (tool-call
* arguments -> modules string -> content string) and the model routinely mangles
* them. So a module may be saved with empty (or `inline_script.` placeholder)
* content; return the ids that still need a body filled out-of-band with
* `set_flow_module_code`.
*/
function emptyInlineScriptModuleIds(editable: EditableFlowJson): string[] {
const value: FlowValue = {
modules: editable.modules,
preprocessor_module: editable.preprocessor_module ?? undefined,
failure_module: editable.failure_module ?? undefined
}
const session = createInlineScriptSession()View on GitHub (pinned to e474e8803c)
Solutions
- For rawscript modules, create the module with content "" and fill the body via set_flow_module_code instead of inlining code.
- Escape embedded code correctly: replace newlines with \n and quotes with \" in the JSON string.
- Validate the JSON string with JSON.parse in a scratch call (or a linter) before sending it to the tool.
- If the value is already an object, pass it as an object instead of a string — the parser only parses strings.
Example fix
// before
modules: '[{ "id": "a", "value": { "content": "print(\"hi\")" } }]' // escaping broke
// after
modules: '[{ "id": "a", "value": { "content": "" } }]' // then set_flow_module_code(a, 'print("hi")') Defensive patterns
Strategy: validation
Validate before calling
function assertParses(field: string, value: unknown) {
if (typeof value === 'string') JSON.parse(value) // throws early with clear context
}
assertParses('modules', modulesArg) Type guard
function isJsonString(s: string): boolean {
try { JSON.parse(s); return true } catch { return false }
} Try / catch
try {
await callTool('modules', modulesArg)
} catch (e) {
if (e.message.startsWith('Invalid JSON for modules')) {
// fall back: create rawscript with empty content, fill body separately
await callTool('modules', modulesArg.replace(/"content":\\?"[\s\S]*?"(?=,|})/g, '"content": ""'))
}
} Prevention
- Never inline multi-line code in a JSON string argument — use empty content + set_flow_module_code
- Run JSON.parse on the payload before sending it
- Pass native objects instead of pre-stringified JSON when the API accepts them
- Escape \n and \" in any embedded code string
When it happens
Trigger: Calling the `modules` or `editable` tool argument with a JSON string whose content is truncated, has raw newlines inside string values (rawscript content), or has unescaped double quotes from the embedded code.
Common situations: An LLM inlines multi-line script code inside the modules JSON string so quotes/newlines break escaping; a template renderer mangles the JSON; a copy-pasted payload is truncated at a newline.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid JSON for ${field}: ${errorMessage}
- Invalid JSON after replacement: ${message}
- Invalid JSON after replacement: ${message}
- Failed to parse or apply JSON: ${error instanceof Error ? er
- {} is not valid json: {}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/6713e8571e63fd38.
Report an issue: GitHub.