grafana/k6 · error

resolving DOM node: %w

Error message

resolving DOM node: %w

What it means

ExecutionContext.adoptBackendNodeID wraps a failed CDP DOM.resolveNode call. k6 resolves a backend node ID into a remote object inside a specific execution context (used when adopting element handles across contexts/frames, e.g. inside waitForSelector). The CDP command fails when the backend node ID is stale (page navigated, node removed), when the execution context ID no longer exists, or when the target session is closing.

Source

Thrown at internal/js/modules/k6/browser/common/execution_context.go:105

// Adopts specified backend node into this execution context from another execution context.
func (e *ExecutionContext) adoptBackendNodeID(backendNodeID cdp.BackendNodeID) (*ElementHandle, error) {
	e.logger.Debugf(
		"ExecutionContext:adoptBackendNodeID",
		"sid:%s stid:%s fid:%s ectxid:%d furl:%q bnid:%d",
		e.sid, e.stid, e.fid, e.id, e.furl, backendNodeID)

	var (
		remoteObj *runtime.RemoteObject
		err       error
	)

	action := dom.ResolveNode().
		WithBackendNodeID(backendNodeID).
		WithExecutionContextID(e.id)

	if remoteObj, err = action.Do(cdp.WithExecutor(e.ctx, e.session)); err != nil {
		return nil, fmt.Errorf("resolving DOM node: %w", err)
	}

	// This can occur due to race conditions between trying to click on an element
	// and chrome moving on (e.g. navigating).
	if remoteObj == nil {
		return nil, fmt.Errorf(`the page may have navigated away or the element is
			now missing. It might happen when k6 and/or Chrome are overloaded. You
			might need to increase the compute resources`)
	}

	return NewJSHandle(e.ctx, e.session, e, e.frame, remoteObj, e.logger).AsElement(), nil
}

// Adopts the specified element handle into this execution context from another execution context.
func (e *ExecutionContext) adoptElementHandle(eh *ElementHandle) (*ElementHandle, error) {
	var (
		efid cdp.FrameID
		esid target.SessionID

View on GitHub (pinned to 93accf6570)

Solutions

  1. Retry the user-level action: re-query the element and act again — the race is usually transient
  2. Await navigations with page.waitForNavigation before touching elements
  3. Give actions an explicit timeout budget so k6's internal retries can absorb the race
  4. If it repeats, reduce parallelism or load on the Chrome instance

Example fix

// before
await page.click('#go');
await frameEl.click(); // races the navigation triggered by #go

// after
await Promise.all([page.waitForNavigation(), page.click('#go')]);
await frameEl.click();
Defensive patterns

Strategy: retry

Try / catch

async function withRetry(fn, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try { return await fn(); }
    catch (e) {
      if (!/resolving DOM node|navigated away|Execution context/.test(e.message) || i === attempts - 1) throw e;
    }
  }
}
await withRetry(() => page.click('#go'));

Prevention

When it happens

Trigger: Adopting an element handle right as the page navigates; the frame containing the element being detached; the execution context destroyed between finding the element and resolving it; Chrome target crash or browser shutdown mid-action.

Common situations: SPAs re-rendering or route-changing during element interaction; elements inside iframes that get removed; races between page.goto and element queries; long scripts where the page closes before the last action completes.

Related errors


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