{"record":{"id":"c9df5ed8a1086ae4","repo":"can1357/oh-my-pi","slug":"markdown-summary-empty-after-normalization","errorCode":null,"errorMessage":"Markdown summary empty after normalization","messagePattern":"Markdown summary empty after normalization","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/commit/conventional/markdown.ts","lineNumber":115,"sourceCode":"\n/** Parse summary output from markdown, XML-ish tags, JSON, or plain text. */\nexport function parseSummaryMarkdown(text: string): string {\n\tif (!text.trim()) return \"\";\n\tconst json = tryJson(text);\n\tif (isRecord(json)) {\n\t\tfor (const key of [\"summary\", \"title\", \"message\"]) {\n\t\t\tconst value = json[key];\n\t\t\tif (typeof value === \"string\" && value.trim()) return stripTypePrefix(value);\n\t\t}\n\t}\n\tconst cleaned = cleanMarkdownText(text);\n\tconst tagged = extractTagLenient(cleaned, \"summary\");\n\tlet summary = tagged ?? cleaned;\n\tsummary = stripHeadingMarkers(summary);\n\tsummary = stripLabelPrefix(summary);\n\tsummary = stripWrappingQuotes(summary);\n\tsummary = summary.split(/\\s+/).filter(Boolean).join(\" \");\n\tif (!summary) throw new Error(\"Markdown summary empty after normalization\");\n\treturn stripTypePrefix(summary);\n}\n\n/** Parse a conventional analysis from llm-git's lenient markdown contract. */\nexport function parseConventionalAnalysisMarkdown(\n\ttext: string,\n\tdefaultType: CommitType = \"chore\",\n): ConventionalAnalysis {\n\tconst payload = tryJson(text);\n\tif (isRecord(payload)) return analysisFromMapping(payload, defaultType);\n\tconst lines = cleanMarkdownText(text).split(/\\r?\\n/);\n\tlet heading: { index: number; type: CommitType; scope: string | null; summary: string } | undefined;\n\tlet coerced: typeof heading;\n\tfor (let index = 0; index < Math.min(5, lines.length); index += 1) {\n\t\tconst line = lines[index] ?? \"\";\n\t\tconst candidate = stripHeadingMarkers(line);\n\t\tconst parsed = parseHeadingLine(candidate, false);\n\t\tif (parsed) {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/commit/conventional/markdown.ts#L97-L133","documentation":"parseSummaryMarkdown takes the model's markdown summary, strips tags/heading markers/label prefixes/quotes, and collapses whitespace. If after all normalization nothing remains, the summary is unusable and this error is thrown (raised by parseSummaryMarkdown, used by generateSummaryFromAnalysis and summary).","triggerScenarios":"Calling parseSummaryMarkdown with a string that normalizes to empty: an empty input, input consisting only of '<summary>...</summary>' tags wrapping nothing, or text made solely of markdown markers ('#', '**', quotes, whitespace).","commonSituations":"Upstream 'Empty model response' or reasoning-only output being passed through, the model echoing only formatting artifacts, or feeding the wrong field (e.g. reasoning text) as the summary.","solutions":["Fix upstream empty responses first — a normalized-empty summary usually means the model returned nothing real","Retry generation with the same prompt","Log the raw model text before parsing to confirm what normalization consumed","Verify the extraction picked the correct section from the model output"],"exampleFix":"// before\nconst summary = parseSummaryMarkdown(rawText);\n// after: guard upstream\nif (!rawText || !rawText.trim()) throw new Error(\"Model returned no summary text\");\nconst summary = parseSummaryMarkdown(rawText);","handlingStrategy":"validation","validationCode":"if (!rawText || !rawText.trim()) throw new Error(\"No summary text from model\");","typeGuard":"function isNonEmptyText(s: string | undefined | null): s is string {\n  return typeof s === \"string\" && s.trim().length > 0;\n}","tryCatchPattern":"try {\n  summary = parseSummaryMarkdown(raw);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Markdown summary empty after normalization\") {\n    logger.warn(\"Model summary normalized to empty\", { raw: raw.slice(0, 200) });\n  }\n  throw err;\n}","preventionTips":["Log raw model output when normalization empties it, to see what was consumed","Fix empty-response issues upstream before touching the parser","Retry generation instead of parsing empty input"],"tags":["markdown","parsing","normalization"],"backgroundTag":"empty-parsed-output","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}