grafana/k6 · warning

optionHandle.dispose: %w

Error message

optionHandle.dispose: %w

What it means

After reading each selected option's value, k6 disposes the option's remote object; a failed Runtime.releaseObject surfaces as 'optionHandle.dispose: <cause>'. This is a resource-cleanup error after the useful work already succeeded — the value was read, but the CDP object could not be released.

Source

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

	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.
	// A solution is similar to the logic in `WaitForLoadState`.

	js := `(html) => {
		window.stop();

View on GitHub (pinned to 93accf6570)

Solutions

  1. If occasional, wrap selectOption and verify the selection afterwards via evaluate instead of trusting the return path.
  2. Avoid closing the page or navigating immediately after selectOption.
  3. If it fires consistently, update k6 — dispose ordering in the selectOption readback has been improved across releases.

Example fix

// before
const v = await page.selectOption('#sel', 'x');
await page.close(); // races dispose

// after
const v = await page.selectOption('#sel', 'x');
await page.waitForTimeout(50); // let cleanup finish
await page.close();
Defensive patterns

Strategy: try-catch

Try / catch

try { return await page.selectOption(sel, v); }
catch (e) {
  if (/optionHandle\.dispose/.test(e.message)) { /* values were read; verify via evaluate */
    return page.evaluate(() => [...document.querySelector(sel).selectedOptions].map(o => o.value));
  }
  throw e;
}

Prevention

When it happens

Trigger: The option's execution context is destroyed between JSONValue() and dispose() (navigation, re-render); the remote object was already garbage-collected or force-released by the browser; the page is closing.

Common situations: Same class as the readback errors: frameworks that rebuild the select on change; page.close() racing the call; mostly harmless unless it fires on every selection, which signals a lifecycle race worth fixing.

Related errors


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