{"record":{"id":"c7ec1829231dc0b6","repo":"windmill-labs/windmill","slug":"invalid-json-after-replacement-message-c7ec18","errorCode":null,"errorMessage":"Invalid JSON after replacement: ${message}","messagePattern":"Invalid JSON after replacement: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":5236,"sourceCode":"\t// content is preserved through the patch via the InlineScriptSession; the\n\t// model uses set_flow_module_code to change inline script bodies.\n\tconst base = await loadFlowDraftValue(path, ctx.workspace)\n\tconst session = createInlineScriptSession()\n\tconst editable = buildEditableFlowJson(flowDraftAsEditableInput(base.flow), session)\n\tconst currentJson = JSON.stringify(editable)\n\tconst updatedJson = findAndReplace(\n\t\tcurrentJson,\n\t\toldString,\n\t\tnewString,\n\t\treplaceAll,\n\t\t'compact flow JSON'\n\t)\n\tlet parsedValue: unknown\n\ttry {\n\t\tparsedValue = JSON.parse(updatedJson)\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\tthrow new Error(`Invalid JSON after replacement: ${message}`)\n\t}\n\n\tconst aiProviders = await getAiAgentProviderCatalogFor(\n\t\tctx.workspace,\n\t\t(parsedValue as { modules?: unknown } | null)?.modules\n\t)\n\tconst aiProviderWarnings: string[] = []\n\tconst patchedEditable = validateEditableFlowJson(parsedValue, { aiProviders, aiProviderWarnings })\n\tconst newFlowValue = applyEditableFlowJsonToFlow(base.flow.value, patchedEditable, session)\n\tfinalizeUnresolvedInlineScripts(newFlowValue)\n\n\tconst result = await writeFlowDraft(\n\t\t{\n\t\t\tpath,\n\t\t\tsummary: base.summary,\n\t\t\tflow: {\n\t\t\t\t...base.flow,\n\t\t\t\tvalue: newFlowValue,","sourceCodeStart":5218,"sourceCodeEnd":5254,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L5218-L5254","documentation":"After applying textual replacements to a flow's JSON representation, the code re-parses the result with JSON.parse. If parsing fails, the replacement produced syntactically invalid JSON (e.g. broke braces/quotes), so the operation is aborted with the parser's message appended. This protects the flow draft from being overwritten with unparseable content.","triggerScenarios":"A patch-style replace operation over a flow JSON string yields text that JSON.parse rejects — mismatched quotes, trailing commas, truncated structure after the replaced snippet.","commonSituations":"The replacement text itself contains unescaped quotes or newline characters; the searched snippet spanned a structural boundary (comma, brace) and its replacement removed or duplicated structural tokens; the model replaced JSON with pseudo-JSON prose.","solutions":["Make the replacement text JSON-escaped (escape quotes/backslashes/newlines) and repeat the patch.","Choose a search snippet that lies entirely within a single JSON string value, not across structural tokens.","Use the structured write_flow tool instead of textual replacement for structural changes."],"exampleFix":"// before\nreplace('\"code\": \"old\"', '\"code\": \"say \"hi\"\"') // unescaped quotes break JSON\n// after\nreplace('\"code\": \"old\"', '\"code\": \"say \\\\\"hi\\\\\"\"')","handlingStrategy":"validation","validationCode":"function canPatchSafely(json: string, search: string, replace: string): boolean {\n  const updated = json.replace(search, replace)\n  try { JSON.parse(updated); return true } catch { return false }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await patchFlowJson(args)\n} catch (e) {\n  if (String(e.message).startsWith('Invalid JSON after replacement:')) {\n    // re-escape replacement text or switch to write_flow\n  } else throw e\n}","preventionTips":["JSON-escape quotes, backslashes and newlines inside replacement strings.","Keep search snippets inside a single JSON string value, never across structural tokens.","Dry-run the replacement locally with JSON.parse before sending.","Prefer structured flow tools (write_flow / patch_flow_json) over raw text replacement."],"tags":["json","parsing","flows"],"backgroundTag":"invalid-json-after-edit","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"}