JuliusBrussee/caveman · error
box model has no quad
Error message
box model has no quad
What it means
Returned by CDPDriver.box() when the fetched dom.BoxModel has fewer than 8 numbers in both its Content quad and its Border quad (a quad is 4 corner points x,y = 8 floats). The driver tries Content first, falls back to Border, then fails: without a quad there is no geometry to compute a click point or bounds from. Happens with elements Chrome reports as displayed:none or detached from layout.
Source
Thrown at browse/cdp.go:318
func (d *CDPDriver) box(ctx context.Context, target Target) (float64, float64, float64, float64, error) {
if target.BackendDOMNodeID <= 0 {
return 0, 0, 0, 0, errors.New("missing backend DOM node id")
}
var model *dom.BoxModel
if err := chromedp.Run(ctx, chromedp.ActionFunc(func(actionCtx context.Context) error {
var err error
model, err = dom.GetBoxModel().WithBackendNodeID(cdp.BackendNodeID(target.BackendDOMNodeID)).Do(actionCtx)
return err
})); err != nil {
return 0, 0, 0, 0, err
}
quad := model.Content
if len(quad) < 8 {
quad = model.Border
}
if len(quad) < 8 {
return 0, 0, 0, 0, errors.New("box model has no quad")
}
minX, maxX := quad[0], quad[0]
minY, maxY := quad[1], quad[1]
for i := 0; i+1 < len(quad); i += 2 {
minX = math.Min(minX, quad[i])
maxX = math.Max(maxX, quad[i])
minY = math.Min(minY, quad[i+1])
maxY = math.Max(maxY, quad[i+1])
}
return (minX + maxX) / 2, (minY + maxY) / 2, maxX - minX, maxY - minY, nil
}
func (d *CDPDriver) visible(ctx context.Context, target Target) (bool, error) {
return d.callBoolOnNode(ctx, target, `function(){ const s = window.getComputedStyle(this); const r = this.getBoundingClientRect(); return !!s && s.display !== "none" && s.visibility !== "hidden" && s.pointerEvents !== "none" && Number(s.opacity || "1") > 0 && r.width > 0 && r.height > 0; }`)
}
func (d *CDPDriver) enabled(ctx context.Context, target Target) (bool, error) {
return d.callBoolOnNode(ctx, target, `function(){ return !(this.disabled === true || this.getAttribute("disabled") !== null || this.getAttribute("aria-disabled") === "true" || this.closest("[inert]")); }`)View on GitHub (pinned to 27d5a3981a)
Solutions
- Make the element actually visible first (expand the section, unhide the container), re-snapshot, then act.
- Retry once after a re-snapshot — if the node was mid-detach during a re-render the new handle usually has a box.
- If the element is inherently box-less (e.g. an <option>), act on it via the select action rather than coordinate-based clicks.
Defensive patterns
Strategy: validation
Validate before calling
// Go: verify the element renders a box before coordinate-dependent action
func hasVisibleBox(driver Driver, ctx context.Context, t Target) bool {
_, _, w, h, err := driver.Box(ctx, t)
return err == nil && w > 0 && h > 0
} Prevention
- Expand/unhide containers (accordions, tabs) and re-snapshot before acting on their contents.
- Use the select action for <option> elements instead of coordinate clicks — options often have no box.
- Retry once after a re-snapshot when a re-render race is suspected.
When it happens
Trigger: Requesting geometry for an element that has no layout box: display:none, inside a closed <details>/hidden container, an option element not rendered, or a node removed from the DOM between the actionable check and the GetBoxModel call.
Common situations: Acting on collapsed accordions/tabs whose contents exist in the AX tree but have no rendered box; racing a re-render that briefly detaches the node; hidden <select> options styled away.
Related errors
- select: missing option
- missing backend DOM node id
- resolve node returned no object
- browser function threw
- browser returned no result object
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/ce25a04611841729.
Report an issue: GitHub.