charmbracelet/crush · error
failed to convert HTML to markdown: %w
Error message
failed to convert HTML to markdown: %w
What it means
The response body was identified as HTML (Content-Type contains text/html) but ConvertHTMLToMarkdown failed to parse/convert it. The raw HTML is cleaned of noisy elements first, then handed to the converter; malformed or exotic HTML that the converter cannot handle produces this wrapped error.
Source
Thrown at internal/agent/tools/fetch_helpers.go:66
if err != nil {
return "", fmt.Errorf("failed to read response body: %w", err)
}
content := string(body)
if !utf8.ValidString(content) {
return "", errors.New("response content is not valid UTF-8")
}
contentType := resp.Header.Get("Content-Type")
// Convert HTML to markdown for better AI processing.
if strings.Contains(contentType, "text/html") {
// Remove noisy elements before conversion.
cleanedHTML := removeNoisyElements(content)
markdown, err := ConvertHTMLToMarkdown(cleanedHTML)
if err != nil {
return "", fmt.Errorf("failed to convert HTML to markdown: %w", err)
}
content = cleanupMarkdown(markdown)
} else if strings.Contains(contentType, "application/json") || strings.Contains(contentType, "text/json") {
// Format JSON for better readability.
formatted, err := FormatJSON(content)
if err == nil {
content = formatted
}
// If formatting fails, keep original content.
}
return content, nil
}
// removeNoisyElements removes script, style, nav, header, footer, and other
// noisy elements from HTML to improve content extraction.
func removeNoisyElements(htmlContent string) string {
doc, err := html.Parse(strings.NewReader(htmlContent))View on GitHub (pinned to 7944b8e522)
Solutions
- Check the wrapped converter error for the specific parse failure
- Verify the page renders valid HTML (validate with an HTML linter or W3C validator)
- If conversion repeatedly fails, fall back to returning the cleaned raw HTML or plain text
- Ensure removeNoisyElements is not corrupting otherwise-valid HTML
Example fix
// before
markdown, err := ConvertHTMLToMarkdown(cleanedHTML)
if err != nil {
return "", fmt.Errorf("failed to convert HTML to markdown: %w", err)
}
// after
markdown, err := ConvertHTMLToMarkdown(cleanedHTML)
if err != nil {
// Fall back to cleaned raw HTML instead of failing the whole fetch.
return cleanupMarkdown(cleanedHTML), nil
} Defensive patterns
Strategy: fallback
Validate before calling
ct := resp.Header.Get("Content-Type")
if strings.Contains(ct, "text/html") {
// proceed with conversion, prepared to fall back
} Type guard
null
Try / catch
markdown, err := ConvertHTMLToMarkdown(cleanedHTML)
if err != nil {
// fall back to cleaned raw HTML or plain text instead of failing
return cleanupMarkdown(cleanedHTML), nil
} Prevention
- Sanitize/clean HTML before conversion (already done via removeNoisyElements)
- Keep the HTML-to-markdown converter updated for malformed input tolerance
- Fall back to raw text when conversion fails rather than erroring the whole fetch
- Check Content-Type before choosing the conversion path
When it happens
Trigger: Content-Type is text/html and ConvertHTMLToMarkdown(cleanedHTML) returns an error — typically severely malformed HTML, encoding issues, or a converter-internal failure.
Common situations: Scraping old or broken web pages with unclosed tags, pages served with wrong charsets, HTML fragments (not full documents), or pages that are actually error pages with odd markup.
Related errors
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/2f831ddea70412c6.
Report an issue: GitHub.