{"record":{"id":"ac108059fdfbef8c","repo":"grafana/k6","slug":"waiting-for-element-state-w","errorCode":null,"errorMessage":"waiting for element state: %w","messagePattern":"waiting for element state: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":1712,"sourceCode":"\t// 4. Enabled\n\t// 5. Receives events\n\tpointerFn := func(apiCtx context.Context, sopts *ScrollIntoViewOptions) (res any, err error) {\n\t\t// We need to scroll the element into view first, otherwise we can\n\t\t// end up in a situation where the element is not in the correct state\n\t\t// (visible and stable, but could be enabled).\n\t\terr = h.scrollRectIntoViewIfNeeded(apiCtx, nil)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"scrolling element into view: %w\", err)\n\t\t}\n\n\t\t// Check if we should run actionability checks\n\t\tif !opts.Force {\n\t\t\t// As mentioned above, if we didn't scroll first, we could end\n\t\t\t// up stuck waiting indefinitely for the element to be in the\n\t\t\t// correct state.\n\t\t\tstates := []string{\"visible\", \"stable\", \"enabled\"}\n\t\t\tif _, err = h.waitForElementState(apiCtx, states, opts.Timeout); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"waiting for element state: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\t// Decide position where a mouse down should happen if needed by action\n\t\tp := opts.Position\n\n\t\t// Change scrolling action depending on the scrolling options\n\t\tif sopts == nil {\n\t\t\tvar rect *dom.Rect\n\t\t\tif p != nil {\n\t\t\t\trect = &dom.Rect{X: p.X, Y: p.Y}\n\t\t\t}\n\t\t\terr = h.scrollRectIntoViewIfNeeded(apiCtx, rect)\n\t\t} else {\n\t\t\t_, err = h.eval(\n\t\t\t\tapiCtx,\n\t\t\t\tevalOptions{forceCallable: true, returnByValue: false},\n\t\t\t\tjs.ScrollIntoView,","sourceCodeStart":1694,"sourceCodeEnd":1730,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/element_handle.go#L1694-L1730","documentation":"Actionability stage of pointer actions (non-force mode): waitForElementState(['visible','stable','enabled']) failed after the initial scroll. The injected waitForElementStates call either timed out (element never satisfies all three states) or returned a DOM error (element detached, not an element). This is the classic 'element is not actionable' failure for click/hover/tap.","triggerScenarios":"Element continuously animating (CSS transitions, carousels) so 'stable' never holds; disabled buttons failing 'enabled'; element toggling visibility on hover/jitter; timeout too short for the app's transitions; element detached mid-wait.","commonSituations":"Buttons with loading spinners that keep re-layouting; sticky elements recalculating position; disabled submit buttons gated on network validation that fails; fast default timeouts against slow staging environments.","solutions":["Increase the action timeout: el.click({ timeout: '30s' })","If the checks themselves are the blocker (e.g. deliberate animation), use force: true to skip the state wait","Disable animations in the test environment or wait for a stable app state before acting","For disabled controls, wait for the enabling condition (el.waitForElementState('enabled')) instead of hammering click"],"exampleFix":"// before\npage.$('#save').click(); // button disabled during autosave\n\n// after\nconst save = page.waitForSelector('#save', { state: 'visible' });\nsave.waitForElementState('enabled', { timeout: '20s' });\nsave.click();","handlingStrategy":"try-catch","validationCode":"const el = page.waitForSelector('#save', { state: 'visible' });\nel.waitForElementState('stable', { timeout: '10s' });\nel.waitForElementState('enabled', { timeout: '10s' });\nel.click();","typeGuard":null,"tryCatchPattern":"try {\n  el.click({ timeout: '30s' });\n} catch (e) {\n  const msg = String(e);\n  if (msg.includes('waiting for element state')) {\n    if (msg.includes('enabled')) throw new Error('control stayed disabled: ' + msg);\n    el.click({ force: true }); // deliberate skip of state checks\n  } else {\n    throw e;\n  }\n}","preventionTips":["Disable non-essential animations in the test environment","Wait for enabled state instead of retrying clicks in a loop","Give the action a timeout sized to the app's longest transition"],"tags":["browser","actionability","timeout","pointer-events","animation"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}