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

  1. Report/reproduce the exact input and stack trace; this is an internal invariant break, not a caller error.
  2. Verify you are using unmodified releases of goquery and golang.org/x/net; upgrade both to latest.
  3. If you need safe error handling instead of a panic, pre-parse with html.ParseFragment yourself or use NewDocumentFromReader paths that return errors normally.
  4. 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

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.

Related errors


AI-assisted analysis of PuerkitoBio/goquery@738783cbc3 (2026-09-06). Data as JSON: /api/errors/7da7fd7be055c402. Report an issue: GitHub.