JuliusBrussee/caveman · error

missing backend DOM node id

Error message

missing backend DOM node id

What it means

Returned by CDPDriver.box() when target.BackendDOMNodeID <= 0. Geometry (center x/y, width, height) is fetched via dom.GetBoxModel().WithBackendNodeID, which requires a real backend node id; a Target without one (never resolved from a snapshot, or zero-valued) cannot be measured, so the driver fails closed instead of issuing a CDP call with id 0.

Source

Thrown at browse/cdp.go:303

		}
		select {
		case <-ctx.Done():
			return 0, 0, ctx.Err()
		case <-time.After(100 * time.Millisecond):
		}
	}
}

type boxPoint struct {
	x float64
	y float64
	w float64
	h float64
}

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]

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Take a fresh snapshot (which rebuilds uid -> BackendDOMNodeID mappings) and retry the action with the new uid.
  2. If you build Target values yourself, resolve them through the driver's snapshot path so BackendDOMNodeID is set.
  3. Treat this as a stale-handle signal: the element reference must be re-acquired, not retried as-is.

Example fix

// before
act(target: "uid-42", action: "click")  // page navigated; uid map stale

// after
snapshot()  // refreshes uid -> BackendDOMNodeID
act(target: "<new-uid>", action: "click")
Defensive patterns

Strategy: validation

Validate before calling

// Go: only attempt box-dependent actions on targets resolved from a live snapshot
func hasBackendNode(t Target) bool {
    return t.BackendDOMNodeID > 0
}

Type guard

func actionableTarget(t Target) (Target, bool) {
    return t, t.BackendDOMNodeID > 0
}

Prevention

When it happens

Trigger: Calling an action or eval path that needs element geometry (click point calculation, bounding-box checks) with a Target struct whose BackendDOMNodeID was never populated — e.g. a uid target built from stale snapshot metadata after a page navigation, or a hand-constructed Target.

Common situations: Acting on a uid from an old snapshot after the page changed; race between snapshot and act; recovery metadata decoded from an expired handle.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/c03da071330aa43c. Report an issue: GitHub.