{"record":{"id":"a12b3b29913171e5","repo":"JuliusBrussee/caveman","slug":"browser-function-threw","errorCode":null,"errorMessage":"browser function threw","messagePattern":"browser function threw","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"browse/cdp.go","lineNumber":415,"sourceCode":"\t\treturn nil, errors.New(\"missing backend DOM node id\")\n\t}\n\tvar res *runtime.RemoteObject\n\tvar exception *runtime.ExceptionDetails\n\tif err := chromedp.Run(ctx, chromedp.ActionFunc(func(actionCtx context.Context) error {\n\t\tobj, err := dom.ResolveNode().WithBackendNodeID(cdp.BackendNodeID(target.BackendDOMNodeID)).Do(actionCtx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif obj == nil {\n\t\t\treturn errors.New(\"resolve node returned no object\")\n\t\t}\n\t\tres, exception, err = runtime.CallFunctionOn(fn).WithObjectID(obj.ObjectID).Do(actionCtx)\n\t\treturn err\n\t})); err != nil {\n\t\treturn nil, err\n\t}\n\tif exception != nil {\n\t\treturn nil, errors.New(\"browser function threw\")\n\t}\n\tif res == nil {\n\t\treturn nil, errors.New(\"browser function returned no result object\")\n\t}\n\treturn res, nil\n}\n\nfunc jsString(s string) string {\n\tb, _ := json.Marshal(s)\n\treturn string(b)\n}\n\nfunc DefaultUserDataDir(home string) string {\n\tif home == \"\" {\n\t\tif h, err := os.UserHomeDir(); err == nil {\n\t\t\thome = filepath.Join(h, \".caveman\")\n\t\t}\n\t}","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/browse/cdp.go#L397-L433","documentation":"Thrown in callOnNodeRaw (browse/cdp.go) when the Runtime.callFunctionOn invocation succeeds at the protocol level but the browser reports an exception detail (e.g. the JS function itself threw). The Go wrapper surfaces this uniformly because protocol errors and JS exceptions arrive on different channels.","triggerScenarios":"Any callOnNode/callBoolOnNode whose injected JS function throws in page context: referencing undefined properties, syntax errors in the generated function body, SecurityError/CORS restrictions, or calling methods absent in the page's runtime.","commonSituations":"Selector matched an element in a different realm (iframe); site CSP or extension wrapping throws; JS built with expectations about a page API that the current page version lacks; JSON string interpolation via jsString produced invalid JS.","solutions":["Inspect what the injected function does and wrap its body in try/catch returning a diagnostic string.","Log/inspect fn (the generated JS) — validate it evaluates in the page console first.","Re-snapshot and retry; the node may have been swapped for a different one mid-call.","Verify the target realm/frame matches where the element lives."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if !jsFunctionSyntaxValid(fn) { return fmt.Errorf(\"generated JS invalid\") }","typeGuard":null,"tryCatchPattern":"out, err := sess.CallOnNode(target, fn)\nvar jsErr *browserJSError\nif errors.As(err, &jsErr) {\n    log.Printf(\"page-side throw in fn %q: %v\", fnName, err)\n    return fallbackValue\n}\nreturn err","preventionTips":["Test the injected JS in the browser console against representative pages.","Wrap generated function bodies defensively (optional chaining, typeof checks).","Build fn via json.Marshal (jsString) instead of string concatenation.","Pin browser and chromedp versions tested against your scripts."],"tags":["cdp","javascript","runtime-exception","browser"],"backgroundTag":"browser-function-threw","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"}