JuliusBrussee/caveman · error

browser function threw

Error message

browser function threw

What it means

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.

Solutions

  1. Inspect what the injected function does and wrap its body in try/catch returning a diagnostic string.
  2. Log/inspect fn (the generated JS) — validate it evaluates in the page console first.
  3. Re-snapshot and retry; the node may have been swapped for a different one mid-call.
  4. Verify the target realm/frame matches where the element lives.
Defensive patterns

Strategy: try-catch

Validate before calling

if !jsFunctionSyntaxValid(fn) { return fmt.Errorf("generated JS invalid") }

Try / catch

out, err := sess.CallOnNode(target, fn)
var jsErr *browserJSError
if errors.As(err, &jsErr) {
    log.Printf("page-side throw in fn %q: %v", fnName, err)
    return fallbackValue
}
return err

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/a12b3b29913171e5. Report an issue: GitHub.

Appendix: 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 3ee70a1026)