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
- Wrap indices: selectOption([{ index: 0 }, { index: 2 }])
- Filter null/undefined out of the array before the call
- Map every item to a string or { value } / { label } / { index } descriptor explicitly
- 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
- Wrap numeric indices as { index: n } before passing
- Filter null/undefined from data-driven arrays
- Map API payloads explicitly to strings or descriptors
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
- parsing select option values: %w
- options: expected array, got %T
- options: expected object, got %T
- options: unsupported type %T
- options[%v]: expected string, got %T
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/0ac4220db25bd658.
Report an issue: GitHub.