{"record":{"id":"e67269b398ec2742","repo":"can1357/oh-my-pi","slug":"markdown-analysis-type-scope-summary-heading-not","errorCode":null,"errorMessage":"Markdown analysis type(scope): summary heading not found","messagePattern":"Markdown analysis type\\(scope\\): summary heading not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/commit/conventional/markdown.ts","lineNumber":143,"sourceCode":"\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) {\n\t\t\theading = { index, ...parsed };\n\t\t\tbreak;\n\t\t}\n\t\tif (!coerced && line.trim().startsWith(\"#\")) {\n\t\t\tconst fallback = parseHeadingLine(candidate, true);\n\t\t\tif (fallback) coerced = { index, ...fallback };\n\t\t}\n\t}\n\theading ??= coerced;\n\tif (!heading) throw new Error(\"Markdown analysis type(scope): summary heading not found\");\n\tconst detailTexts: string[] = [];\n\tconst issueRefs: string[] = [];\n\tfor (const line of lines.slice(heading.index + 1)) {\n\t\tconst stripped = line.trim();\n\t\tif (!stripped) continue;\n\t\tconst lower = stripped.toLowerCase();\n\t\tif (lower.startsWith(\"fixes:\") || lower.startsWith(\"closes:\") || lower.startsWith(\"resolves:\")) {\n\t\t\tconst separator = stripped.indexOf(\":\");\n\t\t\tfor (const ref of stripped.slice(separator + 1).split(\",\")) if (ref.trim()) issueRefs.push(ref.trim());\n\t\t\tcontinue;\n\t\t}\n\t\tconst bullet = stripBullet(stripped);\n\t\tif (!bullet) continue;\n\t\tdetailTexts.push(ensureSentence(bullet));\n\t\tissueRefs.push(...(bullet.match(ISSUE_RE) ?? []));\n\t}\n\treturn conventionalAnalysis({\n\t\ttype: heading.type,","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/commit/conventional/markdown.ts#L125-L161","documentation":"parseConventionalAnalysisMarkdown expects the model's analysis to start with a 'type(scope): summary' heading line. It tries strict heading parsing, then coerces the first markdown '#' line. If no valid heading exists anywhere in the text, this error is thrown (raised by parseConventionalAnalysisMarkdown, called by generateDirectAnalysis, runMapReduce, analysis, canonical).","triggerScenarios":"Calling parseConventionalAnalysisMarkdown on model output that lacks any 'type(scope): subject' line and has no coercible '#' heading — the model replied with prose, JSON, or wrapped the heading in a code fence or label that defeats the parser.","commonSituations":"Prompt drift after model changes (model ignores the heading format instruction), small/fast models not following the conventional-commit format, output wrapped in <summary> tags that weren't stripped, or non-conventional commit style like plain sentences.","solutions":["Retry or switch to a model that reliably follows the type(scope): heading format","Strengthen the reduce prompt's format instructions / add a format example","Verify the raw text isn't wrapped in tags/fences the parser doesn't strip; strip them before parsing","Add leniency: coerce the first non-empty line into the heading if it looks like a conventional commit"],"exampleFix":"// before\nconst analysis = parseConventionalAnalysisMarkdown(raw);\n// after: pre-strip wrappers\nconst cleaned = extractTagLenient(raw, \"analysis\") ?? raw;\nconst analysis = parseConventionalAnalysisMarkdown(cleaned);","handlingStrategy":"try-catch","validationCode":"const headingRe = /^#{0,3}\\s*\\w+(\\([^)]*\\))?!?:\\s.+$/m;\nif (!headingRe.test(raw)) throw new Error(\"Model output missing type(scope): heading\");","typeGuard":null,"tryCatchPattern":"try {\n  const analysis = parseConventionalAnalysisMarkdown(raw);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"heading not found\")) {\n    // regenerate or use a lenient fallback parser\n  }\n  throw err;\n}","preventionTips":["Include an explicit format example in the reduce prompt","Strip <analysis>/<summary> tags and code fences before parsing","Prefer models known to follow structured output formats for analysis"],"tags":["markdown","parsing","conventional-commits"],"backgroundTag":"llm-format-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}