can1357/oh-my-pi · error · Error

Markdown summary empty after normalization

Error message

Markdown summary empty after normalization

What it means

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).

Source

Thrown at packages/coding-agent/src/commit/conventional/markdown.ts:115

/** Parse summary output from markdown, XML-ish tags, JSON, or plain text. */
export function parseSummaryMarkdown(text: string): string {
	if (!text.trim()) return "";
	const json = tryJson(text);
	if (isRecord(json)) {
		for (const key of ["summary", "title", "message"]) {
			const value = json[key];
			if (typeof value === "string" && value.trim()) return stripTypePrefix(value);
		}
	}
	const cleaned = cleanMarkdownText(text);
	const tagged = extractTagLenient(cleaned, "summary");
	let summary = tagged ?? cleaned;
	summary = stripHeadingMarkers(summary);
	summary = stripLabelPrefix(summary);
	summary = stripWrappingQuotes(summary);
	summary = summary.split(/\s+/).filter(Boolean).join(" ");
	if (!summary) throw new Error("Markdown summary empty after normalization");
	return stripTypePrefix(summary);
}

/** Parse a conventional analysis from llm-git's lenient markdown contract. */
export function parseConventionalAnalysisMarkdown(
	text: string,
	defaultType: CommitType = "chore",
): ConventionalAnalysis {
	const payload = tryJson(text);
	if (isRecord(payload)) return analysisFromMapping(payload, defaultType);
	const lines = cleanMarkdownText(text).split(/\r?\n/);
	let heading: { index: number; type: CommitType; scope: string | null; summary: string } | undefined;
	let coerced: typeof heading;
	for (let index = 0; index < Math.min(5, lines.length); index += 1) {
		const line = lines[index] ?? "";
		const candidate = stripHeadingMarkers(line);
		const parsed = parseHeadingLine(candidate, false);
		if (parsed) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix upstream empty responses first — a normalized-empty summary usually means the model returned nothing real
  2. Retry generation with the same prompt
  3. Log the raw model text before parsing to confirm what normalization consumed
  4. Verify the extraction picked the correct section from the model output

Example fix

// before
const summary = parseSummaryMarkdown(rawText);
// after: guard upstream
if (!rawText || !rawText.trim()) throw new Error("Model returned no summary text");
const summary = parseSummaryMarkdown(rawText);
Defensive patterns

Strategy: validation

Validate before calling

if (!rawText || !rawText.trim()) throw new Error("No summary text from model");

Type guard

function isNonEmptyText(s: string | undefined | null): s is string {
  return typeof s === "string" && s.trim().length > 0;
}

Try / catch

try {
  summary = parseSummaryMarkdown(raw);
} catch (err) {
  if (err instanceof Error && err.message === "Markdown summary empty after normalization") {
    logger.warn("Model summary normalized to empty", { raw: raw.slice(0, 200) });
  }
  throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c9df5ed8a1086ae4. Report an issue: GitHub.