{"record":{"id":"c03da071330aa43c","repo":"JuliusBrussee/caveman","slug":"missing-backend-dom-node-id","errorCode":null,"errorMessage":"missing backend DOM node id","messagePattern":"missing backend DOM node id","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"browse/cdp.go","lineNumber":303,"sourceCode":"\t\t}\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn 0, 0, ctx.Err()\n\t\tcase <-time.After(100 * time.Millisecond):\n\t\t}\n\t}\n}\n\ntype boxPoint struct {\n\tx float64\n\ty float64\n\tw float64\n\th float64\n}\n\nfunc (d *CDPDriver) box(ctx context.Context, target Target) (float64, float64, float64, float64, error) {\n\tif target.BackendDOMNodeID <= 0 {\n\t\treturn 0, 0, 0, 0, errors.New(\"missing backend DOM node id\")\n\t}\n\tvar model *dom.BoxModel\n\tif err := chromedp.Run(ctx, chromedp.ActionFunc(func(actionCtx context.Context) error {\n\t\tvar err error\n\t\tmodel, err = dom.GetBoxModel().WithBackendNodeID(cdp.BackendNodeID(target.BackendDOMNodeID)).Do(actionCtx)\n\t\treturn err\n\t})); err != nil {\n\t\treturn 0, 0, 0, 0, err\n\t}\n\tquad := model.Content\n\tif len(quad) < 8 {\n\t\tquad = model.Border\n\t}\n\tif len(quad) < 8 {\n\t\treturn 0, 0, 0, 0, errors.New(\"box model has no quad\")\n\t}\n\tminX, maxX := quad[0], quad[0]\n\tminY, maxY := quad[1], quad[1]","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/browse/cdp.go#L285-L321","documentation":"CDPDriver.box computes an element's bounding box by querying the DOM box model for a backend DOM node ID. If the Target carries no positive BackendDOMNodeID there is nothing to query, so box fails immediately with 'missing backend DOM node id' instead of issuing a doomed dom.GetBoxModel call.","triggerScenarios":"Calling box (indirectly via waitActionable) with a Target whose BackendDOMNodeID is 0 or negative — typically a Target constructed locally or defaulted rather than one obtained from a real DOM resolve/query in the page.","commonSituations":"A caller cached a Target across page navigations and the node id is stale/zero; a code path built a Target struct literal without running the node-discovery step; the element lookup failed upstream and an empty Target was propagated; serialization of a Target dropped the id field.","solutions":["Re-resolve the element to obtain a fresh backend DOM node id before calling actionable/box operations","Fix the code path that constructed the Target so it always populates BackendDOMNodeID from a successful dom query","Invalidate cached Targets after navigation or DOM mutation and re-query","Add an upstream check that BackendDOMNodeID > 0 before queuing actions on a Target"],"exampleFix":"// before\ntarget := Target{} // BackendDOMNodeID == 0\n// after\ntarget, err := d.resolve(selector)\nif err != nil { return err } // target.BackendDOMNodeID > 0","handlingStrategy":"validation","validationCode":"func (t Target) readyForGeometry() bool {\n    return t.BackendDOMNodeID > 0\n}","typeGuard":"func usableTarget(t Target) bool {\n    return t.BackendDOMNodeID > 0\n}\n\nif !usableTarget(t) {\n    t, err = d.resolve(selector)\n    if err != nil { return err }\n}","tryCatchPattern":null,"preventionTips":["Only use Targets obtained from a successful resolve/query call","Never cache Targets across navigations or major DOM mutations","Check BackendDOMNodeID > 0 before queuing actionable operations","Log the Target state when an actionable step fails to spot zero ids early"],"tags":["browser-automation","cdp","dom","invalid-state"],"backgroundTag":"missing-required-argument","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}