grafana/k6 · error

options: expected object, got %T

Error message

options: expected object, got %T

What it means

Defensive branch inside ConvertSelectOptionValues: the values argument exported as a Go map kind, but sobek could not export it into map[string]any. Plain JavaScript object literals always export cleanly, so this guards non-standard map-like values (objects from native bindings, Proxy-wrapped objects, or corrupted runtime values). The rejection surfaces as 'parsing select option values: options: expected object, got <type>'.

Source

Thrown at internal/js/modules/k6/browser/browser/mapping.go:89

				*valOpt.Value = item
				labelOpt := common.SelectOption{Label: new(string)}
				*labelOpt.Label = item
				opts = append(opts, &valOpt, &labelOpt)
			case map[string]any:
				opt, err := extractSelectOptionFromMap(item)
				if err != nil {
					return nil, err
				}

				opts = append(opts, opt)
			default:
				return nil, fmt.Errorf("options: expected string or object, got %T", item)
			}
		}
	case reflect.Map:
		var raw map[string]any
		if err := rt.ExportTo(values, &raw); err != nil {
			return nil, fmt.Errorf("options: expected object, got %T", values)
		}

		opt, err := extractSelectOptionFromMap(raw)
		if err != nil {
			return nil, err
		}

		opts = append(opts, opt)
	case reflect.TypeFor[*common.ElementHandle]().Kind():
		opts = append(opts, t.(*common.ElementHandle)) //nolint:forcetypeassert
	case reflect.TypeFor[sobek.Object]().Kind():
		obj := values.ToObject(rt)
		opt := common.SelectOption{}
		for _, k := range obj.Keys() {
			switch k {
			case "value":
				opt.Value = new(string)
				*opt.Value = obj.Get(k).String()

View on GitHub (pinned to 93accf6570)

Solutions

  1. Pass a plain object literal: selectOption({ value: 'red' })
  2. Rebuild descriptors from primitives: { value: String(obj.value), label: String(obj.label) }
  3. Avoid Proxy or class instances in the values slot
  4. Report reproducible cases to the k6 browser module

Example fix

// before
await page.locator('select').selectOption(proxyDescriptor);

// after
await page.locator('select').selectOption({ value: String(proxyDescriptor.value) });
Defensive patterns

Strategy: type-guard

Validate before calling

function toPlainDescriptor(v) {
  if (typeof v !== 'object' || v === null || Array.isArray(v)) {
    throw new TypeError(`selectOption descriptor must be a plain object, got ${typeof v}`);
  }
  const o = {};
  if ('value' in v && v.value !== undefined) o.value = String(v.value);
  if ('label' in v && v.label !== undefined) o.label = String(v.label);
  if ('index' in v && v.index !== undefined) o.index = Number(v.index);
  return o;
}

Type guard

function isPlainSelectDescriptor(v) {
  return typeof v === 'object' && v !== null &&
    Object.getPrototypeOf(v) === Object.prototype;
}

Try / catch

try {
  await locator.selectOption(descriptor, opts);
} catch (e) {
  if (/expected object/.test(String(e.message))) {
    await locator.selectOption({ value: String(descriptor?.value ?? '') }, opts);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Passing a map-typed sobek value that is not a plain object literal as selectOption's single descriptor, e.g. an object produced by a Go/xk6 extension or a Proxy whose export fails.

Common situations: Interop with xk6 extensions supplying option descriptors; runtime/sobek upgrades altering export semantics.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/c70c1b7059000f55. Report an issue: GitHub.