{"record":{"id":"001ea7903b9c1e32","repo":"grafana/k6","slug":"q-is-not-a-valid-dom-state","errorCode":null,"errorMessage":"%q is not a valid DOM state","messagePattern":"%q is not a valid DOM state","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame_options.go","lineNumber":759,"sourceCode":"\t\tStrict:  false,\n\t\tTimeout: defaultTimeout,\n\t}\n}\n\n// Parse parses the frame waitForSelector options.\nfunc (o *FrameWaitForSelectorOptions) Parse(ctx context.Context, opts sobek.Value) error {\n\trt := k6ext.Runtime(ctx)\n\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 \"state\":\n\t\t\t\tstate := opts.Get(k).String()\n\t\t\t\tif s, ok := domElementStateToID[state]; ok {\n\t\t\t\t\to.State = s\n\t\t\t\t} else {\n\t\t\t\t\treturn fmt.Errorf(\"%q is not a valid DOM state\", state)\n\t\t\t\t}\n\t\t\tcase \"strict\":\n\t\t\t\to.Strict = opts.Get(k).ToBoolean()\n\t\t\tcase \"timeout\":\n\t\t\t\to.Timeout = time.Duration(opts.Get(k).ToInteger()) * time.Millisecond\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// FrameDispatchEventOptions are options for Frame.dispatchEvent.\ntype FrameDispatchEventOptions struct {\n\t*FrameBaseOptions\n}\n\n// NewFrameDispatchEventOptions returns a new FrameDispatchEventOptions.","sourceCodeStart":741,"sourceCodeEnd":777,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame_options.go#L741-L777","documentation":"FrameWaitForSelectorOptions.Parse (frame_options.go:759) looks up the `state` option in domElementStateToID (frame.go:52-57), which defines exactly four valid states: \"attached\", \"detached\", \"visible\", \"hidden\". An unknown string aborts waitForSelector with this error before any waiting happens. The check is case-sensitive local validation, not a DOM query result.","triggerScenarios":"frame.waitForSelector('#el', { state: 'shown' }), { state: 'Visible' }, { state: 'displayed' }, or values borrowed from other frameworks like 'attached-to-dom'. Default when omitted is \"visible\".","commonSituations":"Porting selectors logic from Selenium/Playwright vocabulary ('visible' vs 'displayed', 'attached' vs 'present'), or driving the state value from external test data that has not been constrained.","solutions":["Use one of: \"attached\", \"detached\", \"visible\", \"hidden\" (lowercase)","Remember the default is \"visible\"; if you got the error you likely set an explicit invalid value","Constrain state in shared constants/TS types so only the four values can be passed"],"exampleFix":"// before\nframe.waitForSelector('.status', { state: 'shown' });\n\n// after\nframe.waitForSelector('.status', { state: 'visible' });\n// or wait for removal from DOM:\nframe.waitForSelector('.loading', { state: 'detached' });","handlingStrategy":"validation","validationCode":"const STATES = ['attached', 'detached', 'visible', 'hidden'];\nif (opts && opts.state !== undefined && !STATES.includes(opts.state)) {\n  throw new Error(`state must be one of ${STATES.join(', ')} (got ${opts.state})`);\n}\nframe.waitForSelector(selector, opts);","typeGuard":"function isDomState(v: unknown): v is 'attached' | 'detached' | 'visible' | 'hidden' {\n  return v === 'attached' || v === 'detached' || v === 'visible' || v === 'hidden';\n}","tryCatchPattern":null,"preventionTips":["Default state is 'visible' — only pass state when you need a different condition","Map vocabulary from other frameworks: displayed→visible, present→attached, gone→detached"],"tags":["browser","options","dom-state","waitforselector","validation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}