grafana/k6 · error · TypeError

${name} constructor must be called with new

Error message

${name} constructor must be called with new

What it means

After the isEditable action succeeds (frame.go:1438), k6 asserts the returned value is a bool; otherwise it returns 'checking is %q editable: unexpected type %T'. The underlying waitForElementState call returns a Go bool, so a non-bool (typically nil) means the action machinery produced an unexpected value: usually a race where the frame/context died between resolution and result, or a k6 browser-module bug in value plumbing. It is an internal invariant break, not a normal test failure.

Source

Thrown at internal/js/modules/k6/experimental/streams/sobek.go:22

	"fmt"
	"reflect"
	"slices"

	"github.com/grafana/sobek"

	"go.k6.io/k6/v2/js/common"
	"go.k6.io/k6/v2/js/modules"
)

// newWebIDLConstructor wraps a native Sobek constructor so it follows Web IDL call semantics.
// Sobek otherwise treats a native constructor call without `new` exactly like construction and
// does not expose a NewTarget that lets the native function distinguish the two cases.
func newWebIDLConstructor(rt *sobek.Runtime, name string, constructor any) (sobek.Value, error) {
	factory, err := rt.RunString(`
(function(constructor, name) {
  const wrapper = function(...args) {
    if (new.target === undefined) {
      throw new TypeError(name + " constructor must be called with new");
    }
    return Reflect.construct(constructor, args, new.target);
  };
  Object.defineProperty(wrapper, "name", { value: name, configurable: true });
  return wrapper;
})`)
	if err != nil {
		return nil, err
	}

	call, ok := sobek.AssertFunction(factory)
	if !ok {
		return nil, newError(RuntimeError, "Web IDL constructor wrapper factory is not a function")
	}

	return call(sobek.Undefined(), rt.ToValue(constructor), rt.ToValue(name))
}

View on GitHub (pinned to 01ffac6f24)

Solutions

  1. Retry the check once after waiting for a stable state (waitForLoadState or locator.waitFor) — most instances are transient races.
  2. Make sure the frame is not being navigated while the check runs.
  3. Reproduce with K6_DEBUG=true to capture the wrapped context.
  4. If reproducible, open a grafana/k6 issue with the script, page, and k6 version.

Example fix

// before
const ok = await page.isEditable('#comment');

// after
async function isEditableStable(sel) {
  for (let i = 0; i < 3; i++) {
    try { return await page.isEditable(sel); }
    catch (e) { if (!String(e).includes('unexpected type')) throw e; }
  }
  throw new Error('isEditable unstable: ' + sel);
}
const ok = await isEditableStable('#comment');
Defensive patterns

Strategy: retry

Validate before calling

await page.waitForLoadState('domcontentloaded'); // avoid checking while navigation is in flight

Try / catch

try {
  return await page.isEditable(sel);
} catch (e) {
  if (!String(e).includes('unexpected type')) throw e;
  await page.waitForLoadState('domcontentloaded');
  return await page.isEditable(sel); // transient race; retry once
}

Prevention

When it happens

Trigger: Frame detaches or navigates between selector resolution and the state result; the action returns a nil result on a rare error path; DevTools protocol/session teardown races during the check; k6 browser-module regression.

Common situations: Flaky failures in CI on slow machines during page transitions; races in SPA tests where checks run exactly as iframes swap; after a k6 version upgrade introducing a conversion bug.

Related errors


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