JuliusBrussee/caveman · error

select: missing option

Error message

select: missing option

What it means

The browse CDP driver's Act 'select' action requires an option value: it reads req.Option, falling back to req.Text, and if both are empty it fails closed with 'select: missing option' rather than dispatching a meaningless JS selection. The subsequent injected script only selects an option whose value, label, or trimmed text equals the wanted string.

Solutions

  1. Populate req.Option with the target <option>'s value (or its visible text) before dispatching the select action
  2. Alternatively set req.Text to the option's visible label if you only know the text
  3. Validate the select's options in the page first (via JS evaluation) to get an exact value/label match
  4. Return a clearer tool-schema error upstream so empty selections are rejected before reaching the driver

Example fix

// before
req := ActRequest{Action: "select"} // Option and Text empty
// after
req := ActRequest{Action: "select", Option: "california"}
Defensive patterns

Strategy: validation

Validate before calling

def build_select_request(value: str | None, text: str | None) -> dict:
    if not value and not text:
        raise ValueError("select requires an option value or text")
    return {"action": "select", "option": value or "", "text": text or ""}

Type guard

func validSelectTarget(req ActRequest) bool {
    return req.Option != "" || req.Text != ""
}

Prevention

When it happens

Trigger: Calling the driver's Act with action type 'select' and leaving both Option and Text fields as empty strings on the request struct; no option is sent, so the guard raises before any CDP callOnNode dispatch.

Common situations: Marshalling an MCP/tool payload where the 'option' key was omitted or empty; a caller built the request programmatically and forgot to copy the desired value; an upstream tool mapped a null selection to an empty string instead of rejecting earlier.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at browse/cdp.go:205

		if err := chromedp.Run(runCtx, chromedp.ActionFunc(func(actionCtx context.Context) error {
			return input.InsertText(req.Text).Do(actionCtx)
		})); err != nil {
			return ActionResult{OK: false, Settled: false}, err
		}
		return dispatchedAction(), nil
	case "select":
		if err := d.scrollIntoView(runCtx, target); err != nil {
			return ActionResult{OK: false, Settled: false}, err
		}
		if _, _, err := d.waitActionable(runCtx, target); err != nil {
			return ActionResult{OK: false, Settled: false}, err
		}
		option := req.Option
		if option == "" {
			option = req.Text
		}
		if option == "" {
			return ActionResult{OK: false, Settled: false}, errors.New("select: missing option")
		}
		if err := d.callOnNode(runCtx, target, fmt.Sprintf(`function(){ const wanted = %s; const options = Array.from(this.options || []); const match = options.find(o => o.value === wanted || o.label === wanted || o.textContent.trim() === wanted); if (!match) { throw new Error("option not found"); } this.value = match.value; this.dispatchEvent(new Event("input", {bubbles:true})); this.dispatchEvent(new Event("change", {bubbles:true})); return true; }`, jsString(option))); err != nil {
			return ActionResult{OK: false, Settled: false}, err
		}
		return dispatchedAction(), nil
	case "scroll":
		if err := d.scrollIntoView(runCtx, target); err != nil {
			return ActionResult{OK: false, Settled: false}, err
		}
		if _, _, err := d.waitActionable(runCtx, target); err != nil {
			return ActionResult{OK: false, Settled: false}, err
		}
		return dispatchedAction(), nil
	default:
		return ActionResult{OK: false, Settled: false}, fmt.Errorf("unknown action: %s", req.Action)
	}
}

View on GitHub (pinned to 3ee70a1026)