{"record":{"id":"99f259849840d4e7","repo":"siyuan-note/siyuan","slug":"html-to-text-panicked-v","errorCode":null,"errorMessage":"HTML to text panicked: %v","messagePattern":"HTML to text panicked: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/webfetch.go","lineNumber":135,"sourceCode":"\t}\n\n\treturn truncateRunes(result, maxWebFetchChars), nil\n}\n\nfunc safeHTML2Markdown(engine *lute.Lute, htmlStr string) (result string, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"HTML to Markdown panicked: %v\", r)\n\t\t}\n\t}()\n\tresult, err = engine.HTML2Markdown(htmlStr)\n\treturn\n}\n\nfunc safeHTML2Text(engine *lute.Lute, htmlStr string) (result string, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"HTML to text panicked: %v\", r)\n\t\t}\n\t}()\n\tresult = engine.HTML2Text(htmlStr)\n\treturn\n}\n\nfunc truncateRunes(s string, maxChars int) string {\n\trunes := []rune(s)\n\tif len(runes) <= maxChars {\n\t\treturn s\n\t}\n\treturn string(runes[:maxChars]) + \"\\n\\n...content truncated, total length \" + fmt.Sprintf(\"%d\", len(runes)) + \" characters...\"\n}\n\nfunc extractFilename(rawURL, contentType string) string {\n\tu, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn gulu.Rand.String(7) + extByContentType(contentType)","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/webfetch.go#L117-L153","documentation":"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.","triggerScenarios":"Same class as 1187 but on the text-extraction path (format=\"text\"): pathological nesting, obfuscated HTML, lute internal fault.","commonSituations":"Choosing format=\"text\" to dodge a Markdown-conversion panic, only to hit a different lute panic; lute version regression.","solutions":["Rely on the automatic raw-HTML fallback (result==\"\" returns htmlStr unchanged) — the user still gets content.","Upgrade lute and rebuild.","Reduce the input to a minimal reproducer and report to 88250/lute.","If you need the error surfaced, capture it at the call site instead of discarding with `_`."],"exampleFix":"// before (webfetch.go:106): error discarded\nresult, _ = safeHTML2Text(engine, htmlStr)\n\n// after: surface the text-path error for diagnostics\nresult, txtErr := safeHTML2Text(engine, htmlStr)\nif txtErr != nil {\n    logging.LogErrorf(\"html2text failed: %s\", txtErr)\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func isHTML2TextPanic(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"HTML to text panicked:\")\n}","tryCatchPattern":"// NOTE: at webfetch.go:106 the text-path error is discarded (`result, _ = ...`),\n// so callers never see this string today — a panic here silently yields the\n// raw-HTML fallback. If you surface the error, handle it as a non-fatal fallback.\nout, err := util.WebFetch(raw, \"text\")\nif err != nil {\n    // fall back to the raw HTML returned on the empty-result branch\n}","preventionTips":["Remember the text path's error is currently discarded — rely on the raw-HTML fallback rather than expecting this error externally.","Upgrade lute to pick up HTML2Text panic fixes.","Reduce the triggering HTML and report it to 88250/lute."],"tags":["panic","lute","parsing","recover","text"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}