grafana/k6 · error

reading value: %w

Error message

reading value: %w

What it means

While converting the selected <option> handles into strings, k6 calls JSONValue() on each option handle; failure is reported as 'reading value: <cause>'. This means the option handle became invalid (its execution context or remote object was released) before its value could be serialized.

Source

Thrown at internal/js/modules/k6/browser/common/frame.go:1740

	v, err := call(f.ctx, act, opts.Timeout)
	if err != nil {
		return nil, errorFromDOMError(err)
	}
	selectHandle, ok := v.(jsHandle)
	if !ok {
		return nil, fmt.Errorf("unexpected select element type %T", v)
	}

	// pack the selected <option> values inside <select> into a slice
	optHandles, err := selectHandle.getProperties()
	if err != nil {
		return nil, fmt.Errorf("getProperties: %w", err)
	}
	vals := make([]string, 0, len(optHandles))
	for _, oh := range optHandles {
		val, err := oh.JSONValue()
		if err != nil {
			return nil, fmt.Errorf("reading value: %w", err)
		}
		vals = append(vals, val)
		if err := oh.dispose(); err != nil {
			return nil, fmt.Errorf("optionHandle.dispose: %w", err)
		}
	}
	if err := selectHandle.dispose(); err != nil {
		return nil, fmt.Errorf("selectHandle.dispose: %w", err)
	}

	return vals, nil
}

// SetContent replaces the entire HTML document content.
func (f *Frame) SetContent(html string, _ *FrameSetContentOptions) error {
	f.log.Debugf("Frame:SetContent", "fid:%s furl:%q", f.ID(), f.URL())

	// TODO(@inancgumus): Respect the FrameSetContentOptions before executing the action.

View on GitHub (pinned to 93accf6570)

Solutions

  1. Re-select after the DOM settles: wait for the framework render to finish (waitForTimeout or a predicate) before selectOption.
  2. Read values defensively afterwards with evaluate(() => select.selectedOptions.map(o => o.value)) instead of relying on the return value.
  3. If the framework replaces options on change, set the value programmatically and dispatch the event yourself.

Example fix

// before
const vals = await page.selectOption('#sel', 'x'); // re-render invalidates options

// after
await page.selectOption('#sel', 'x').catch(() => null);
const vals = await page.evaluate(() =>
  [...document.querySelector('#sel').selectedOptions].map(o => o.value));
Defensive patterns

Strategy: fallback

Validate before calling

await page.waitForFunction(() => !document.querySelector('#sel')?.disabled);

Try / catch

try { return await page.selectOption(sel, v); }
catch (e) {
  if (/reading value/.test(e.message)) {
    return page.evaluate(() => [...document.querySelector(sel).selectedOptions].map(o => o.value));
  }
  throw e;
}

Prevention

When it happens

Trigger: The <select> is rebuilt (framework re-render) between selection and value readback, invalidating option remote objects; navigation destroys the context; the handle was already disposed due to the page state changing mid-loop.

Common situations: React/Vue controlled selects that re-render options after change events; option elements removed by virtual DOM diffing; slow pages where the readback exceeds object lifetime.

Related errors


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