{"record":{"id":"6d3d4b03f516d9b1","repo":"can1357/oh-my-pi","slug":"no-json-payload-found-in-response","errorCode":null,"errorMessage":"No JSON payload found in response","messagePattern":"No JSON payload found in response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/commit/utils.ts","lineNumber":23,"sourceCode":"\treturn message.content.find(content => content.type === \"toolCall\" && content.name === name) as ToolCall | undefined;\n}\n\nexport function extractTextContent(message: AssistantMessage): string {\n\treturn message.content\n\t\t.filter(content => content.type === \"text\")\n\t\t.map(content => content.text)\n\t\t.join(\"\")\n\t\t.trim();\n}\n\nexport function parseJsonPayload(text: string): unknown {\n\tconst trimmed = text.trim();\n\tif (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\")) {\n\t\treturn JSON.parse(trimmed) as unknown;\n\t}\n\tconst match = trimmed.match(/\\{[\\s\\S]*\\}/);\n\tif (!match) {\n\t\tthrow new Error(\"No JSON payload found in response\");\n\t}\n\treturn JSON.parse(match[0]) as unknown;\n}\n\nexport function normalizeAnalysis(parsed: {\n\ttype: ConventionalAnalysis[\"type\"];\n\tscope: string | null;\n\tdetails: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;\n\tissue_refs: string[];\n}): ConventionalAnalysis {\n\treturn {\n\t\ttype: parsed.type,\n\t\tscope: parsed.scope?.trim() || null,\n\t\tdetails: parsed.details.map(detail => ({\n\t\t\ttext: detail.text.trim(),\n\t\t\tchangelogCategory: detail.user_visible ? detail.changelog_category : undefined,\n\t\t\tuserVisible: detail.user_visible ?? false,\n\t\t})),","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/commit/utils.ts#L5-L41","documentation":"parseJsonPayload extracts a JSON object from raw model text. It accepts a pure JSON object, or finds the first {...} block via regex; if no {...} substring exists at all it throws. This guards the commit analysis pipeline against responses containing no JSON object.","triggerScenarios":"Calling parseJsonPayload (via parsed() or runEvalCompletion) with model output that contains no brace-delimited object — e.g. plain prose, a refusal message, an empty string, or the model describing results without emitting JSON.","commonSituations":"Model refused the task or answered conversationally instead of following the JSON format instruction; response truncated before any '{'; prompt template drifted so the JSON instruction was lost; tiny local model ignoring the schema.","solutions":["Retry generation with a stronger model or higher instruction adherence.","Inspect the raw response logged upstream to confirm the model ignored the JSON format and fix the prompt.","Ask the model to output only JSON (no prose, no code fences) or strip fences before parsing.","Handle the throw upstream and fall back to a manual/default commit flow."],"exampleFix":"// before: model replied 'I cannot analyze this diff.'\nparseJsonPayload(response) // throws\n// after: prompt ends with\n'Respond with a single JSON object and nothing else.'","handlingStrategy":"validation","validationCode":"function hasJsonPayload(text: string): boolean {\n  const t = text.trim();\n  if (t.startsWith(\"{\") && t.endsWith(\"}\")) return true;\n  return /\\{[\\s\\S]*\\}/.test(t);\n}","typeGuard":"function isJsonObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}","tryCatchPattern":"try {\n  const analysis = parseJsonPayload(rawResponse);\n} catch (err) {\n  if (err instanceof Error && err.message === \"No JSON payload found in response\") {\n    // log rawResponse, retry generation or fall back to default flow\n  } else throw err;\n}","preventionTips":["End prompts with an explicit 'respond with a single JSON object and nothing else' instruction.","Strip markdown code fences from model output before parsing.","Log raw model responses so non-JSON answers are diagnosable.","Prefer models with structured-output/JSON mode for analysis steps."],"tags":["json","parsing","llm-output","validation"],"backgroundTag":"no-json-in-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}