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

  1. Use the official goquery release; rebuild from a clean checkout of the module.
  2. If you forked the code, add the missing case to the switch in getChildrenWithSiblingType (traversal.go) for any new siblingType constant.
  3. 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

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.