grafana/k6 · error

options[%v]: expected string, got %T

Error message

options[%v]: expected string, got %T

What it means

Thrown by extractSelectOptionFromMap when a selectOption descriptor object has a value or label key that is not a string. The value and label fields must be strings (index must be an integer); anything else rejects with 'options[<key>]: expected string, got <type>' (e.g. options[value]: expected string, got float64), wrapped by the mapping as 'parsing select option values: ...'.

Source

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

		*labelOpt.Label = t.(string) //nolint:forcetypeassert
		opts = append(opts, &valOpt, &labelOpt)
	default:
		return nil, fmt.Errorf("options: unsupported type %T", values)
	}

	return opts, nil
}

func extractSelectOptionFromMap(v map[string]any) (*common.SelectOption, error) {
	opt := &common.SelectOption{}
	for k, raw := range v {
		switch k {
		case "value":
			opt.Value = new(string)

			v, ok := raw.(string)
			if !ok {
				return nil, fmt.Errorf("options[%v]: expected string, got %T", k, raw)
			}

			*opt.Value = v
		case "label":
			opt.Label = new(string)

			v, ok := raw.(string)
			if !ok {
				return nil, fmt.Errorf("options[%v]: expected string, got %T", k, raw)
			}
			*opt.Label = v
		case "index":
			opt.Index = new(int64)

			switch raw := raw.(type) {
			case int:
				*opt.Index = int64(raw)
			case int64:

View on GitHub (pinned to 93accf6570)

Solutions

  1. Stringify value and label: selectOption({ value: String(id) })
  2. Use { index: n } (number) for positional selection, not { value: n }
  3. Drop null/undefined keys from descriptors instead of passing them
  4. Validate descriptor objects against { value?: string, label?: string, index?: number } before the call

Example fix

// before
await page.locator('select').selectOption({ value: 42 });

// after
await page.locator('select').selectOption({ value: '42' }); // or { index: 42 }
Defensive patterns

Strategy: type-guard

Validate before calling

function normalizeDescriptor(d) {
  const o = {};
  if (d.value !== undefined && d.value !== null) {
    if (typeof d.value !== 'string') throw new TypeError(`descriptor value must be a string, got ${typeof d.value}`);
    o.value = d.value;
  }
  if (d.label !== undefined && d.label !== null) {
    if (typeof d.label !== 'string') throw new TypeError(`descriptor label must be a string, got ${typeof d.label}`);
    o.label = d.label;
  }
  if (d.index !== undefined) o.index = Number(d.index);
  return o;
}

Type guard

function isSelectDescriptor(v) {
  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 (/options\[.*\]: expected string/.test(String(e.message))) {
    throw new Error(`Descriptor value/label must be strings; use { index: n } for numbers: ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: selectOption({ value: 2 }) — numeric value instead of string; selectOption({ label: null }); selectOption([{ value: ['a'] }]) — array-valued descriptor field; only index may be numeric.

Common situations: JSON fixtures where option values are numbers; sloppy destructuring leaving undefined/null in descriptors; mixing index and value conventions in one descriptor with wrong types.

Related errors


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