{"record":{"id":"d219e0d0cd370fcb","repo":"grafana/k6","slug":"name-constructor-must-be-called-with-new","errorCode":null,"errorMessage":"${name} constructor must be called with new","messagePattern":"(.+?) constructor must be called with new","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/experimental/streams/sobek.go","lineNumber":22,"sourceCode":"\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\n\t\"github.com/grafana/sobek\"\n\n\t\"go.k6.io/k6/v2/js/common\"\n\t\"go.k6.io/k6/v2/js/modules\"\n)\n\n// newWebIDLConstructor wraps a native Sobek constructor so it follows Web IDL call semantics.\n// Sobek otherwise treats a native constructor call without `new` exactly like construction and\n// does not expose a NewTarget that lets the native function distinguish the two cases.\nfunc newWebIDLConstructor(rt *sobek.Runtime, name string, constructor any) (sobek.Value, error) {\n\tfactory, err := rt.RunString(`\n(function(constructor, name) {\n  const wrapper = function(...args) {\n    if (new.target === undefined) {\n      throw new TypeError(name + \" constructor must be called with new\");\n    }\n    return Reflect.construct(constructor, args, new.target);\n  };\n  Object.defineProperty(wrapper, \"name\", { value: name, configurable: true });\n  return wrapper;\n})`)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcall, ok := sobek.AssertFunction(factory)\n\tif !ok {\n\t\treturn nil, newError(RuntimeError, \"Web IDL constructor wrapper factory is not a function\")\n\t}\n\n\treturn call(sobek.Undefined(), rt.ToValue(constructor), rt.ToValue(name))\n}\n","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/js/modules/k6/experimental/streams/sobek.go#L4-L40","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the check once after waiting for a stable state (waitForLoadState or locator.waitFor) — most instances are transient races.","Make sure the frame is not being navigated while the check runs.","Reproduce with K6_DEBUG=true to capture the wrapped context.","If reproducible, open a grafana/k6 issue with the script, page, and k6 version."],"exampleFix":"// before\nconst ok = await page.isEditable('#comment');\n\n// after\nasync function isEditableStable(sel) {\n  for (let i = 0; i < 3; i++) {\n    try { return await page.isEditable(sel); }\n    catch (e) { if (!String(e).includes('unexpected type')) throw e; }\n  }\n  throw new Error('isEditable unstable: ' + sel);\n}\nconst ok = await isEditableStable('#comment');","handlingStrategy":"retry","validationCode":"await page.waitForLoadState('domcontentloaded'); // avoid checking while navigation is in flight","typeGuard":null,"tryCatchPattern":"try {\n  return await page.isEditable(sel);\n} catch (e) {\n  if (!String(e).includes('unexpected type')) throw e;\n  await page.waitForLoadState('domcontentloaded');\n  return await page.isEditable(sel); // transient race; retry once\n}","preventionTips":["Don't run state checks concurrently with navigation or iframe swaps.","Stabilize the page (waitForLoadState/locator.waitFor) before asserting state.","Capture K6_DEBUG logs when it flakes to identify the racing event."],"tags":["k6","browser","iseditable","type-assertion","race"],"backgroundTag":"unexpected-eval-return-type","analyzedSha":"01ffac6f245854c1b8adc6a69857c76a15f90022","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}