grafana/k6 · error

dispatching element event

Error message

dispatching element event %q: %w

What it means

ElementHandle.dispatchEvent() wraps the failure of an action that evaluates an injected script (injected.dispatchEvent(node, type, eventInit)) in the element's execution context via Runtime.callFunctionOn. No actionability preconditions are checked, so failures come from the evaluation itself, the default timeout (30s), or a dead execution context.

Solutions

  1. Re-acquire the element handle immediately before dispatching, after any navigation
  2. Keep eventInit a plain JSON-serializable object ({ bubbles: true, detail: {...} })
  3. Increase the default timeout via the timeout option or K6_BROWSER_TIMEOUT env var
  4. Wait for the page to settle: page.waitForLoadState('networkidle') before dispatching

Example fix

// before
const el = await page.$('input#q');
await page.goto('https://app.example.com/search');
await el.dispatchEvent('input', { bubbles: true }); // stale handle

// after
await page.goto('https://app.example.com/search');
const el = await page.$('input#q');
await el.dispatchEvent('input', { bubbles: true, detail: { value: 'k6' } });
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the handle is still live before dispatching
const alive = await el.evaluate((e) => e.isConnected).catch(() => false);
if (!alive) {
  throw new Error('element detached; re-query before dispatchEvent');
}
await el.dispatchEvent('input', { bubbles: true });

Try / catch

try {
  await el.dispatchEvent('input', { bubbles: true, detail: { v: 1 } });
} catch (e) {
  if (/Execution context|Could not find object|timed out/i.test(String(e.message))) {
    await page.waitForLoadState();
    const fresh = await page.$(sel);
    await fresh.dispatchEvent('input', { bubbles: true, detail: { v: 1 } });
  } else throw e;
}

Prevention

When it happens

Trigger: The element handle is detached (navigation or SPA re-render) so callFunctionOn fails with 'Could not find object with given id'; the page navigates while the script runs, destroying the execution context; the default timeout expires; eventInit contains non-serializable values (functions, DOM nodes, cyclic objects).

Common situations: Dispatching custom events in SPAs right after route changes; passing complex eventInit payloads built from page objects; handles cached from an earlier page state reused after navigation.

Related errors


AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18). Data as JSON: /api/errors/d476d0f5ece26eb1. Report an issue: GitHub.

Appendix: source

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

		return fmt.Errorf("double clicking on element: %w", err)
	}

	applySlowMo(h.ctx)

	return nil
}

// DispatchEvent dispatches a DOM event to the element.
func (h *ElementHandle) DispatchEvent(typ string, eventInit any) error {
	dispatchEvent := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
		return handle.dispatchEvent(apiCtx, typ, eventInit)
	}
	opts := NewElementHandleBaseOptions(h.DefaultTimeout())
	dispatchEventAction := h.newAction(
		[]string{}, dispatchEvent, opts.Force, withRetry, opts.NoWaitAfter, opts.Timeout,
	)
	if _, err := call(h.ctx, dispatchEventAction, opts.Timeout); err != nil {
		return fmt.Errorf("dispatching element event %q: %w", typ, err)
	}

	applySlowMo(h.ctx)

	return nil
}

// Fill types the given value into the element.
func (h *ElementHandle) Fill(value string, opts *ElementHandleBaseOptions) error {
	fill := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
		return nil, handle.fill(apiCtx, value)
	}
	fillAction := h.newAction(
		[]string{"visible", "enabled", "editable"},
		fill, opts.Force, withRetry, opts.NoWaitAfter, opts.Timeout,
	)
	if _, err := call(h.ctx, fillAction, opts.Timeout); err != nil {
		return fmt.Errorf("filling element: %w", err)

View on GitHub (pinned to 3fcf5388d7)