PuerkitoBio/goquery · warning
goquery: failed to parse HTML:
Error message
goquery: failed to parse HTML:
What it means
parseHtmlWithContext panics when html.ParseFragment returns an error while parsing an HTML fragment into a context node. Per golang.org/x/net/html, ParseFragment only errors on a reader failure other than EOF, and since parsing reads from a strings.Reader this should practically never happen — the panic is a defensive assertion for an impossible condition.
Source
Thrown at manipulation.go:571
contents.wrapAllNodes(ns...)
} else {
s.AppendNodes(cloneNode(ns[0]))
}
})
return s
}
func parseHtml(h string) []*html.Node {
return parseHtmlWithContext(h, &html.Node{Type: html.ElementNode})
}
func parseHtmlWithContext(h string, context *html.Node) []*html.Node {
// Errors are only returned when the io.Reader returns any error besides
// EOF, but strings.Reader never will
nodes, err := html.ParseFragment(strings.NewReader(h), context)
if err != nil {
panic("goquery: failed to parse HTML: " + err.Error())
}
return nodes
}
// Get the first child that is an ElementNode
func getFirstChildEl(n *html.Node) *html.Node {
c := n.FirstChild
for c != nil && c.Type != html.ElementNode {
c = c.NextSibling
}
return c
}
// Deep copy a slice of nodes.
func cloneNodes(ns []*html.Node) []*html.Node {
cns := make([]*html.Node, 0, len(ns))
for _, n := range ns {View on GitHub (pinned to 738783cbc3)
Solutions
- Report/reproduce the exact input and stack trace; this is an internal invariant break, not a caller error.
- Verify you are using unmodified releases of goquery and golang.org/x/net; upgrade both to latest.
- If you need safe error handling instead of a panic, pre-parse with html.ParseFragment yourself or use NewDocumentFromReader paths that return errors normally.
- Recover from the panic at a boundary if processing untrusted input through a fork.
Example fix
// before
doc.Find("div").WrapAllHtml(userSuppliedHtml) // panics on parse failure in forks
// after
func safeWrapAll(doc *goquery.Document, sel, htmlStr string) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("fragment parse failed: %v", r)
}
}()
doc.Find(sel).WrapAllHtml(htmlStr)
return nil
} Defensive patterns
Strategy: try-catch
Try / catch
defer func() {
if r := recover(); r != nil {
if errStr, ok := r.(string); ok && strings.HasPrefix(errStr, "goquery: failed to parse HTML:") {
err = errors.New(errStr)
return
}
panic(r)
}
}() Prevention
- Use official goquery and golang.org/x/net releases; this panic is practically unreachable otherwise.
- Keep fragment HTML as plain strings (strings.Reader never errors), which is what goquery already does.
- If processing a fork, convert panics to errors at your API boundary with defer/recover.
When it happens
Trigger: Indirectly triggered by any manipulation API that parses HTML fragments: AfterHtml, BeforeHtml, PrependHtml, AppendHtml, WrapAllHtml, WrapInnerHtml, ReplaceWithHtml, or parseHtml/cachedParseHtmlWithContext — only if strings.NewReader somehow fails, which cannot occur for in-memory strings.
Common situations: Effectively unreachable in real use; if a stack trace surfaces it, it usually indicates a custom/altered fork of the library, memory corruption, or a modified golang.org/x/net/html package where ParseFragment returns non-EOF errors.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of PuerkitoBio/goquery@738783cbc3 (2026-09-06).
Data as JSON: /api/errors/7da7fd7be055c402.
Report an issue: GitHub.