siyuan-note/siyuan · error

HTML to Markdown conversion failed

Error message

HTML to Markdown conversion failed: %s

What it means

When format is 'markdown' (the default), WebFetch converts the fetched HTML with lute's HTML2Markdown via the panic-protected safeHTML2Markdown wrapper; a non-nil conversion error is wrapped as 'HTML to Markdown conversion failed: <cause>'. This indicates the Lute engine rejected or failed on the specific HTML content, not a network problem.

Solutions

  1. Retry with format="text" which uses safeHTML2Text and never returns a conversion error
  2. Inspect the wrapped cause after 'HTML to Markdown conversion failed: ' for the specific Lute failure
  3. Sanitize or simplify the HTML upstream if you control the source page
  4. Check the Lute version if the same URL previously converted fine — behavior may have changed upstream

Example fix

// before
md, err := WebFetch(url, "markdown") // conversion fails on malformed HTML

// after
text, err := WebFetch(url, "text") // fall back to plain-text extraction
if err == nil {
    md = text
}
Defensive patterns

Strategy: fallback

Try / catch

md, err := WebFetch(url, "markdown")
if err != nil && strings.Contains(err.Error(), "conversion failed") {
    md, err = WebFetch(url, "text") // text path has no conversion error return
}

Prevention

When it happens

Trigger: engine.HTML2Markdown returns an error for pathological or malformed HTML (deeply nested structures, encoding oddities, input the converter cannot process).

Common situations: Fetching pages with heavily malformed tag soup; documents with exotic encodings that survived the fetch as invalid UTF-8; upstream Lute version changes altering conversion behavior on edge-case markup.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/7b577c767d450162. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/webfetch.go:109

	isHTML := strings.HasPrefix(contentType, "text/html")
	if !isHTML {
		return truncateRunes(htmlStr, maxWebFetchChars), nil
	}

	if htmlStr == "" {
		return "", nil
	}

	engine := NewLute()
	var result string
	switch format {
	case "text":
		result, _ = safeHTML2Text(engine, htmlStr)
	default: // markdown
		md, mdErr := safeHTML2Markdown(engine, htmlStr)
		if mdErr != nil {
			return "", errors.New("HTML to Markdown conversion failed: " + mdErr.Error())
		}
		result = md
	}

	if result == "" {
		return htmlStr, nil
	}

	return truncateRunes(result, maxWebFetchChars), nil
}

func safeHTML2Markdown(engine *lute.Lute, htmlStr string) (result string, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("HTML to Markdown panicked: %v", r)
		}
	}()
	result, err = engine.HTML2Markdown(htmlStr)

View on GitHub (pinned to 9f775e8a12)