JuliusBrussee/caveman · error
browser function threw
Error message
browser function threw
What it means
Returned by callOnNodeRaw() when runtime.CallFunctionOn reports non-nil ExceptionDetails — the injected JavaScript function threw inside the page. The comment deliberately discards the details object, so the Go-side message is generic; the actual exception (e.g. 'option not found' from the select action's matcher, or any throw in a custom eval function) happened in browser JS.
Source
Thrown at browse/cdp.go:415
return nil, errors.New("missing backend DOM node id")
}
var res *runtime.RemoteObject
var exception *runtime.ExceptionDetails
if err := chromedp.Run(ctx, chromedp.ActionFunc(func(actionCtx context.Context) error {
obj, err := dom.ResolveNode().WithBackendNodeID(cdp.BackendNodeID(target.BackendDOMNodeID)).Do(actionCtx)
if err != nil {
return err
}
if obj == nil {
return errors.New("resolve node returned no object")
}
res, exception, err = runtime.CallFunctionOn(fn).WithObjectID(obj.ObjectID).Do(actionCtx)
return err
})); err != nil {
return nil, err
}
if exception != nil {
return nil, errors.New("browser function threw")
}
if res == nil {
return nil, errors.New("browser function returned no result object")
}
return res, nil
}
func jsString(s string) string {
b, _ := json.Marshal(s)
return string(b)
}
func DefaultUserDataDir(home string) string {
if home == "" {
if h, err := os.UserHomeDir(); err == nil {
home = filepath.Join(h, ".caveman")
}
}View on GitHub (pinned to 27d5a3981a)
Solutions
- For select: verify the exact option value/label via snapshot or eval before selecting; the match checks value, then label, then trimmed textContent.
- For custom functions: run the same expression via an eval tool to see the real JS exception (this error intentionally hides it), then fix the function.
- Add existence guards in your injected JS (return a sentinel instead of throwing) if you need to distinguish 'not found' from hard failure.
Example fix
// before act(target: "s3", action: "select", option: "USA") // label is "United States" // after act(target: "s3", action: "select", text: "United States")
Defensive patterns
Strategy: try-catch
Validate before calling
// Go: confirm the option exists before selecting
const probe = `function(){ const w = %s; return Array.from(this.options||[]).some(o => o.value === w || o.label === w || o.textContent.trim() === w); }` Try / catch
// Go: the Go error hides details; re-run the same expression via eval to surface the JS exception
if err != nil && strings.Contains(err.Error(), "browser function threw") {
detail, _ := d.eval(ctx, fn) // log detail, fix the JS or the option string
return fmt.Errorf("browser function threw: %s", detail)
} Prevention
- Match select options on their exact value first; fall back to label, then trimmed text.
- Return sentinel values from injected JS instead of throwing when 'not found' is an expected outcome.
- Smoke-test injected function bodies with an eval call before wiring them into actions.
When it happens
Trigger: The select action's inline function throwing 'option not found' because no <option> matches the given value/label/text; a custom eval/on-node function that throws (TypeError on unexpected DOM shape, accessing removed nodes); assertion-style helpers that throw to signal failure.
Common situations: Selecting an option whose label changed or that does not exist in this <select>; evaluating functions against a DOM that re-rendered mid-call; brittle selectors assuming child structure that changed.
Related errors
- select: missing option
- missing backend DOM node id
- box model has no quad
- resolve node returned no object
- browser function returned no result object
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/a12b3b29913171e5.
Report an issue: GitHub.