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

  1. For select: verify the exact option value/label via snapshot or eval before selecting; the match checks value, then label, then trimmed textContent.
  2. 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.
  3. 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

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


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