siyuan-note/siyuan · error

HTML to text panicked: %v

Error message

HTML to text panicked: %v

What it means

The panic-derived error from safeHTML2Text (webfetch.go:132-138). engine.HTML2Text panicked and the deferred recover() caught it, becoming fmt.Errorf("HTML to text panicked: %v", r). Note safeHTML2Text's err return value is currently discarded at the call site (webfetch.go:106: `result, _ = safeHTML2Text(...)`) so this specific error string is not propagated to the caller — a panic here silently yields an empty result, which then falls through to the raw HTML fallback at webfetch.go:115-117.

Source

Thrown at kernel/util/webfetch.go:135

	}

	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)
	if len(runes) <= maxChars {
		return s
	}
	return string(runes[:maxChars]) + "\n\n...content truncated, total length " + fmt.Sprintf("%d", len(runes)) + " characters..."
}

func extractFilename(rawURL, contentType string) string {
	u, err := url.Parse(rawURL)
	if err != nil {
		return gulu.Rand.String(7) + extByContentType(contentType)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Rely on the automatic raw-HTML fallback (result=="" returns htmlStr unchanged) — the user still gets content.
  2. Upgrade lute and rebuild.
  3. Reduce the input to a minimal reproducer and report to 88250/lute.
  4. If you need the error surfaced, capture it at the call site instead of discarding with `_`.

Example fix

// before (webfetch.go:106): error discarded
result, _ = safeHTML2Text(engine, htmlStr)

// after: surface the text-path error for diagnostics
result, txtErr := safeHTML2Text(engine, htmlStr)
if txtErr != nil {
    logging.LogErrorf("html2text failed: %s", txtErr)
}
Defensive patterns

Strategy: fallback

Type guard

func isHTML2TextPanic(err error) bool {
    return err != nil && strings.HasPrefix(err.Error(), "HTML to text panicked:")
}

Try / catch

// NOTE: at webfetch.go:106 the text-path error is discarded (`result, _ = ...`),
// so callers never see this string today — a panic here silently yields the
// raw-HTML fallback. If you surface the error, handle it as a non-fatal fallback.
out, err := util.WebFetch(raw, "text")
if err != nil {
    // fall back to the raw HTML returned on the empty-result branch
}

Prevention

When it happens

Trigger: Same class as 1187 but on the text-extraction path (format="text"): pathological nesting, obfuscated HTML, lute internal fault.

Common situations: Choosing format="text" to dodge a Markdown-conversion panic, only to hit a different lute panic; lute version regression.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/99f259849840d4e7. Report an issue: GitHub.