{"record":{"id":"04db8e3b6924e483","repo":"grafana/k6","slug":"q-is-not-a-valid-lifecycle","errorCode":null,"errorMessage":"%q is not a valid lifecycle","messagePattern":"%q is not a valid lifecycle","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/page_options.go","lineNumber":81,"sourceCode":"\t\tTimeout:   defaultTimeout,\n\t}\n}\n\n// Parse parses the page go back/forward options.\nfunc (o *PageGoBackForwardOptions) Parse(ctx context.Context, opts sobek.Value) error {\n\tif common.IsNullish(opts) {\n\t\treturn nil\n\t}\n\n\tobj := opts.ToObject(k6ext.Runtime(ctx))\n\tfor _, k := range obj.Keys() {\n\t\tswitch k {\n\t\tcase \"waitUntil\":\n\t\t\tlifeCycle := obj.Get(k).String()\n\t\t\tif l, ok := lifecycleEventToID[lifeCycle]; ok {\n\t\t\t\to.WaitUntil = l\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"%q is not a valid lifecycle\", lifeCycle)\n\t\t\t}\n\t\tcase \"timeout\":\n\t\t\to.Timeout = time.Duration(obj.Get(k).ToInteger()) * time.Millisecond\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Parse parses the page reload options.\nfunc (o *PageReloadOptions) Parse(ctx context.Context, opts sobek.Value) error {\n\trt := k6ext.Runtime(ctx)\n\tif !common.IsNullish(opts) {\n\t\topts := opts.ToObject(rt)\n\t\tfor _, k := range opts.Keys() {\n\t\t\tswitch k {\n\t\t\tcase \"waitUntil\":\n\t\t\t\tlifeCycle := opts.Get(k).String()","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/page_options.go#L63-L99","documentation":"PageGotoOptions.Parse validates the waitUntil option of page.goto() against the allowlist {'load', 'domcontentloaded', 'networkidle'} (map lifecycleEventToID). An exact, case-sensitive miss returns this error before any navigation starts; it is deterministic input validation.","triggerScenarios":"page.goto(url, { waitUntil: 'networkIdle' }) — casing error; 'domContentLoaded'; or Playwright-style 'commit'/'domcontentloaded ' with whitespace. Any value not exactly load/domcontentloaded/networkidle fails.","commonSituations":"Porting scripts from Playwright/Puppeteer that use different lifecycle names or casings; typos; trailing whitespace from templated config.","solutions":["Use exactly 'load', 'domcontentloaded', or 'networkidle' (all lowercase)","Prefer 'domcontentloaded' for faster, more stable waits; note networkidle is discouraged for loading pages"],"exampleFix":"// before\nawait page.goto('https://example.com/', { waitUntil: 'networkIdle' });\n\n// after\nawait page.goto('https://example.com/', { waitUntil: 'networkidle' });","handlingStrategy":"validation","validationCode":"const WAIT_UNTIL = ['load', 'domcontentloaded', 'networkidle'];\nif (!WAIT_UNTIL.includes(waitUntil)) throw new Error(`invalid waitUntil: ${waitUntil}`);","typeGuard":"const isValidWaitUntil = (v) => ['load','domcontentloaded','networkidle'].includes(v);","tryCatchPattern":null,"preventionTips":["Define WAIT_UNTIL once per script and reuse it in every goto/reload","Remember values are lowercase and case-sensitive"],"tags":["browser","navigation","validation","lifecycle","user-input"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}