grafana/k6 · error

disposing document element: %w

Error message

disposing document element: %w

What it means

Cleanup error inside ElementHandle.ownerFrame(): the temporary document-element handle acquired during the call could not be disposed in the deferred cleanup, and the error is joined (errors.Join) with the function's return error. Disposal fails when the remote object was already released - most commonly because a navigation destroyed the execution context between evaluation and dispose - or because the CDP session/page is closing.

Source

Thrown at internal/js/modules/k6/browser/common/element_handle.go:1144

	opts := evalOptions{
		forceCallable: true,
		returnByValue: false,
	}
	res, err := h.evalWithScript(h.ctx, opts, fn)
	if err != nil {
		return nil, fmt.Errorf("getting document element: %w", err)
	}
	if res == nil {
		return nil, errors.New("getting document element: nil document")
	}

	documentHandle, ok := res.(*ElementHandle)
	if !ok {
		return nil, fmt.Errorf("unexpected result type while getting document element: %T", res)
	}
	defer func() {
		if err := documentHandle.Dispose(); err != nil {
			err = fmt.Errorf("disposing document element: %w", err)
			rerr = errors.Join(err, rerr)
		}
	}()

	if documentHandle.remoteObject.ObjectID == "" {
		return nil, err
	}

	var node *cdp.Node
	action := dom.DescribeNode().WithObjectID(documentHandle.remoteObject.ObjectID)
	if node, err = action.Do(cdp.WithExecutor(h.ctx, h.session)); err != nil {
		return nil, fmt.Errorf("getting node in frame: %w", err)
	}
	if node == nil || node.FrameID == "" {
		return nil, fmt.Errorf("no frame found for node: %w", err)
	}

	frame, ok := h.frame.manager.getFrameByID(node.FrameID)

View on GitHub (pinned to 93accf6570)

Solutions

  1. Stabilize the page before calling: await page.waitForLoadState('load')
  2. Re-query the element handle immediately before ownerFrame()
  3. Treat the failure as transient: retry ownerFrame() once after the page settles
  4. Upgrade k6 - dispose failures are joined so they surface; newer releases handle context destruction more gracefully

Example fix

// before
const f = await h.ownerFrame();
// after
await page.waitForLoadState('load');
let f;
try {
  f = await h.ownerFrame();
} catch (e) {
  f = await (await page.$('#in-frame')).ownerFrame(); // retry with a fresh handle
}
Defensive patterns

Strategy: try-catch

Validate before calling

await page.waitForLoadState('load');
const el = await page.$('#in-frame');
if (el) {
  const frame = await el.ownerFrame();
}

Try / catch

try {
  frame = await handle.ownerFrame();
} catch (e) {
  if (String(e).includes('disposing document element')) {
    await page.waitForLoadState('load');
    frame = await (await page.$(sel)).ownerFrame(); // retry with fresh handle
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: The page navigates between the getDocumentElement evaluation and the deferred Dispose; the browser context or page is closed while ownerFrame() runs; the remote object was already garbage-collected/released; the CDP session dropped.

Common situations: ownerFrame() racing a redirect or form submit; iframe content reloading concurrently; browser shutdown at the end of a scenario while frame resolution is in flight.

Related errors


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