windmill-labs/windmill · error
Invalid JSON for ${field}: ${errorMessage}
Error message
Invalid JSON for ${field}: ${errorMessage} What it means
parseOptionalJsonArg accepts a value that may already be parsed or a JSON string, and throws when JSON.parse of the string fails, embedding the underlying parser message. It is used by parsedModule and flowTools to coerce string tool-call arguments into objects.
Source
Thrown at frontend/src/lib/components/copilot/chat/flow/core.ts:250
)
const specialFlowModuleFields = {
preprocessor_module: SPECIAL_MODULE_IDS.PREPROCESSOR,
failure_module: SPECIAL_MODULE_IDS.FAILURE
} as const
type SpecialFlowModuleField = keyof typeof specialFlowModuleFields
function parseOptionalJsonArg(value: unknown, field: string): unknown {
if (value === undefined || value === null) {
return value
}
try {
return typeof value === 'string' ? JSON.parse(value) : value
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e)
throw new Error(`Invalid JSON for ${field}: ${errorMessage}`)
}
}
function validateSpecialFlowModule(
module: unknown,
field: SpecialFlowModuleField
): FlowModule | null | undefined {
if (module === undefined || module === null) {
return module
}
const result = flowModuleSchema.safeParse(module)
if (!result.success) {
const errors = result.error.issues.slice(0, 5).map((issue) => {
const path = issue.path.length > 0 ? issue.path.join('.') : field
return `${path}: ${issue.message}`
})
throw new Error(`Invalid ${field}:\n${errors.join('\n')}`)View on GitHub (pinned to e474e8803c)
Solutions
- Validate/repair the JSON string before calling (JSON.parse in a try, or a lenient repair like jsonrepair).
- Have the model retry the tool call with the argument as a proper JSON object rather than a string.
- Check for truncation if the string looks cut off mid-structure and re-issue with fewer/larger chunks.
Example fix
// before
await flowTools.updateFlow({ flow: rawStringFromModel })
// after
let flowArg
try { flowArg = JSON.parse(rawStringFromModel) }
catch { flowArg = JSON.parse(jsonrepair(rawStringFromModel)) }
await flowTools.updateFlow({ flow: flowArg }) Defensive patterns
Strategy: validation
Validate before calling
let parsed: unknown
if (typeof value === 'string') {
try { parsed = JSON.parse(value) } catch { parsed = JSON.parse(jsonrepair(value)) }
} else parsed = value Type guard
function isJsonObject(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v)
} Try / catch
try {
await tool(args)
} catch (e) {
if (String(e.message).startsWith('Invalid JSON for')) {
return requestModelRetryWithJsonInstruction(e.message)
}
throw e
} Prevention
- Pass already-parsed objects to tool calls instead of JSON strings when possible.
- Instruct the model to emit strict JSON (no trailing commas, double quotes).
- Check for token-limit truncation when long JSON arguments fail to parse.
When it happens
Trigger: A tool argument for `field` arrives as a string that is not valid JSON — typically an LLM-produced tool call with truncated, single-quoted, or commented JSON — and typeof value === 'string'.
Common situations: Model emits JSON with trailing commas or unquoted keys; argument was truncated by a token limit; caller double-encoded (passed a JSON string of a JSON string) or passed plain text where JSON was expected.
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
- Completed jobs file must contain an array of jobs
- Queued jobs file must contain an array of jobs
- Invalid JSON after replacement: ${message}
- Invalid JSON for ${field}: ${message}${hint}
- Invalid JSON after replacement: ${message}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/caf8ed9a14c00b8b.
Report an issue: GitHub.