siyuan-note/siyuan · error

HTML to Markdown panicked

Error message

HTML to Markdown panicked: %v

What it means

safeHTML2Markdown wraps lute's HTML2Markdown with a recover() so a panic inside the conversion engine (index out of range, nil deref on malformed input) is converted into this error instead of crashing the kernel. It signals an engine bug or input that trips an unguarded path in Lute.

Solutions

  1. The kernel already survives the panic — handle the returned error and fall back to raw HTML (WebFetch itself returns htmlStr when result is empty)
  2. Reproduce with the same HTML and report/trim the offending markup
  3. Retry with format="text" to bypass the Markdown converter path
  4. Check for a Lute update if a previously-working URL started panicking

Example fix

// before
md, err := WebFetch(url, "markdown")

// after
md, err := WebFetch(url, "markdown")
if err != nil && strings.Contains(err.Error(), "panicked") {
    md, _ = WebFetch(url, "text") // avoid the panicking converter path
}
Defensive patterns

Strategy: try-catch

Try / catch

md, err := WebFetch(url, "markdown")
if err != nil && strings.Contains(err.Error(), "panicked") {
    // engine panicked on this input; fall back to text or raw HTML
    md, _ = WebFetch(url, "text")
}

Prevention

When it happens

Trigger: HTML2Markdown panics on a specific HTML payload — typically deeply nested or malformed markup that hits an unguarded code path in the Lute engine.

Common situations: Fetching attacker-controlled or user-generated HTML with pathological nesting; Lute regression where a previously fine document panics after an engine update; fuzz-like input from scraped web pages.

Related errors


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

Appendix: source

Thrown at kernel/util/webfetch.go:124

	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)
	return
}

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

func truncateRunes(s string, maxChars int) string {
	runes := []rune(s)

View on GitHub (pinned to 9f775e8a12)