grafana/k6 · error

options: expected string or object, got %T

Error message

options: expected string or object, got %T

What it means

Thrown by ConvertSelectOptionValues when an item inside the selectOption values array is neither a string nor a plain object. Each array element must be a string (matches value or label) or a map descriptor with value/label/index; anything else — number, boolean, null, nested array — rejects with 'options: expected string or object, got <type>', wrapped by the mapping as 'parsing select option values: ...'.

Source

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

		for _, item := range sl {
			switch item := item.(type) {
			case string:
				// Strings will match values or labels
				valOpt := common.SelectOption{Value: new(string)}
				*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)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Wrap indices: selectOption([{ index: 0 }, { index: 2 }])
  2. Filter null/undefined out of the array before the call
  3. Map every item to a string or { value } / { label } / { index } descriptor explicitly
  4. Validate external data shape before feeding it to the browser module

Example fix

// before
await page.locator('select.size').selectOption([0, null, 2]);

// after
await page.locator('select.size').selectOption([{ index: 0 }, { index: 2 }]);
Defensive patterns

Strategy: type-guard

Validate before calling

function sanitizeSelectItems(items) {
  return items
    .filter((it) => it !== null && it !== undefined)
    .map((it) => {
      if (typeof it === 'string') return it;
      if (typeof it === 'number') return { index: it };
      if (typeof it === 'object' && !Array.isArray(it)) return it;
      throw new TypeError(`selectOption array items must be string or object, got ${typeof it}`);
    });
}

Type guard

function isSelectOptionItem(v) {
  if (typeof v === 'string') return true;
  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;
  return (v.value === undefined || typeof v.value === 'string') &&
         (v.label === undefined || typeof v.label === 'string') &&
         (v.index === undefined || typeof v.index === 'number');
}

Try / catch

try {
  await locator.selectOption(values, opts);
} catch (e) {
  if (/expected string or object/.test(String(e.message))) {
    throw new Error(`selectOption array may contain only strings or {value,label,index} objects: ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: selectOption([0, 2]) — raw indices instead of { index: n } descriptors; selectOption(['red', null]) — null item; selectOption([true]) or [['red']] — boolean/nested-array items.

Common situations: Data-driven tests mapping API responses straight into selectOption where the payload contains numbers or nulls; assuming zero-based indices can be passed raw; sloppy spread of optional values (...maybeValues) that can inject undefined/null.

Related errors


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