PuerkitoBio/goquery · warning
Invalid sibling type.
Error message
Invalid sibling type.
What it means
This panic is thrown inside goquery's internal sibling iterator (getChildrenWithSiblingType in traversal.go) when the siblingType enum value st does not match any known case (siblingAll, siblingPrev*, siblingNext*, etc.). It is an internal invariant violation: there is no public API that legitimately produces an unknown siblingType, so hitting it means internal state is corrupted or the library was modified.
Source
Thrown at traversal.go:647
ret = skipNode.NextSibling
}
}
case siblingPrev, siblingPrevAll, siblingPrevUntil:
if cur == nil {
// Start with previous sibling of the skip node
ret = skipNode.PrevSibling
} else {
ret = cur.PrevSibling
}
case siblingNext, siblingNextAll, siblingNextUntil:
if cur == nil {
// Start with next sibling of the skip node
ret = skipNode.NextSibling
} else {
ret = cur.NextSibling
}
default:
panic("Invalid sibling type.")
}
if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
return
}
// Not a valid node, try again from this one
cur = ret
}
}
// For the cases that collect every matching sibling, count them in a
// cheap pointer walk first so the result slice can be sized exactly,
// avoiding repeated slice growth. The Until cases are skipped (counting
// would require running the predicate twice) and so are the single-result
// Next/Prev cases.
switch st {
case siblingAll, siblingAllIncludingNonElements, siblingPrevAll, siblingNextAll:
n := 0
for c := iter(nil); c != nil; c = iter(c) {View on GitHub (pinned to 738783cbc3)
Solutions
- Use the official goquery release; rebuild from a clean checkout of the module.
- If you forked the code, add the missing case to the switch in getChildrenWithSiblingType (traversal.go) for any new siblingType constant.
- Capture the stack trace and report it upstream with the exact call chain.
Example fix
// before (fork adds a new type but no case)
const siblingFiltered siblingType = 99
// after (handle it in getChildrenWithSiblingType)
case siblingFiltered:
if cur == nil {
ret = skipNode.NextSibling
} else {
ret = cur.NextSibling
}
if ret != nil && ret.Type != html.ElementNode {
ret = nil // apply filter semantics
} Defensive patterns
Strategy: try-catch
Try / catch
defer func() {
if r := recover(); r != nil {
if errStr, ok := r.(string); ok && errStr == "Invalid sibling type." {
err = fmt.Errorf("goquery internal error: %v", r)
return
}
panic(r)
}
}() Prevention
- Do not fork or patch goquery's traversal internals; if you must, extend the siblingType switch completely.
- Pin and verify module checksums (go.sum) so a tampered build cannot introduce invalid enum values.
- Wrap bulk scraping operations in a recover boundary so one internal panic does not kill a long-running crawler.
When it happens
Trigger: Not reachable through correct public API usage. Traversal calls Next*, Prev*, Siblings*, Parents*, Children* (Until/WithNodes variants) all pass valid constants; the panic fires only if a fork or patch introduces a new siblingType without adding a switch case.
Common situations: Developers patching or forking goquery's traversal.go; custom builds where a new sibling type constant is defined but not handled in the iterator's switch; corrupted binary from a bad build.
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/12541cac9398805a.
Report an issue: GitHub.