{"record":{"id":"6713e8571e63fd38","repo":"windmill-labs/windmill","slug":"invalid-json-for-field-message-hint","errorCode":null,"errorMessage":"Invalid JSON for ${field}: ${message}${hint}","messagePattern":"Invalid JSON for (.+?): (.+?)(.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":528,"sourceCode":"})\n\n// modules/preprocessor_module/failure_module can carry rawscript `content`, whose\n// quotes and newlines are the usual reason the JSON string fails to parse.\nconst FLOW_CODE_BEARING_FIELDS = new Set(['modules', 'preprocessor_module', 'failure_module'])\n\nfunction parseOptionalJsonArg(value: unknown, field: string): unknown {\n\tif (value === undefined || value === null) {\n\t\treturn value\n\t}\n\n\ttry {\n\t\treturn typeof value === 'string' ? JSON.parse(value) : value\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tconst hint = FLOW_CODE_BEARING_FIELDS.has(field)\n\t\t\t? ' 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.'\n\t\t\t: ''\n\t\tthrow new Error(`Invalid JSON for ${field}: ${message}${hint}`)\n\t}\n}\n\n/**\n * Rawscript bodies are fragile to embed inside the `modules` JSON string: the\n * code's quotes and newlines have to survive three levels of escaping (tool-call\n * arguments -> modules string -> content string) and the model routinely mangles\n * them. So a module may be saved with empty (or `inline_script.` placeholder)\n * content; return the ids that still need a body filled out-of-band with\n * `set_flow_module_code`.\n */\nfunction emptyInlineScriptModuleIds(editable: EditableFlowJson): string[] {\n\tconst value: FlowValue = {\n\t\tmodules: editable.modules,\n\t\tpreprocessor_module: editable.preprocessor_module ?? undefined,\n\t\tfailure_module: editable.failure_module ?? undefined\n\t}\n\tconst session = createInlineScriptSession()","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L510-L546","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmodules: '[{ \"id\": \"a\", \"value\": { \"content\": \"print(\\\"hi\\\")\" } }]' // escaping broke\n// after\nmodules: '[{ \"id\": \"a\", \"value\": { \"content\": \"\" } }]' // then set_flow_module_code(a, 'print(\"hi\")')","handlingStrategy":"validation","validationCode":"function assertParses(field: string, value: unknown) {\n  if (typeof value === 'string') JSON.parse(value) // throws early with clear context\n}\nassertParses('modules', modulesArg)","typeGuard":"function isJsonString(s: string): boolean {\n  try { JSON.parse(s); return true } catch { return false }\n}","tryCatchPattern":"try {\n  await callTool('modules', modulesArg)\n} catch (e) {\n  if (e.message.startsWith('Invalid JSON for modules')) {\n    // fall back: create rawscript with empty content, fill body separately\n    await callTool('modules', modulesArg.replace(/\"content\":\\\\?\"[\\s\\S]*?\"(?=,|})/g, '\"content\": \"\"'))\n  }\n}","preventionTips":["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"],"tags":["json","parsing","escaping","ai-tools"],"backgroundTag":"invalid-json","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"}