{"record":{"id":"cd9c9158b085bf9f","repo":"grafana/k6","slug":"cannot-do-key-up-w","errorCode":null,"errorMessage":"cannot do key up: %w","messagePattern":"cannot do key up: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/keyboard.go","lineNumber":277,"sourceCode":"\nfunc (k *Keyboard) comboPress(keys string, opts KeyboardOptions) error {\n\tif opts.Delay > 0 {\n\t\tif err := wait(k.ctx, opts.Delay); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tkk := split(keys)\n\tfor _, key := range kk {\n\t\tif err := k.down(key); err != nil {\n\t\t\treturn fmt.Errorf(\"cannot do key down: %w\", err)\n\t\t}\n\t}\n\n\tfor i := range kk {\n\t\tkey := kk[len(kk)-i-1]\n\t\tif err := k.up(key); err != nil {\n\t\t\treturn fmt.Errorf(\"cannot do key up: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// This splits the string on `+`.\n// If `+` on it's own is passed, it will return [\"+\"].\n// If `++` is passed in, it will return [\"+\", \"\"].\n// If `+++` is passed in, it will return [\"+\", \"+\"].\nfunc split(keys string) []string {\n\tvar (\n\t\tkk = make([]string, 0)\n\t\ts  strings.Builder\n\t)\n\tfor _, r := range keys {\n\t\tif r == '+' && s.Len() > 0 {\n\t\t\tkk = append(kk, s.String())","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/keyboard.go#L259-L295","documentation":"Returned by comboPress when the key-up leg fails. All key-downs already succeeded and were dispatched; the up leg replays the segments in reverse order, and this error wraps the first failing up(). Because downs completed, a failure here can leave keys logically pressed in the page even though local pressedKeys tracking was updated.","triggerScenarios":"press('Control+A') where the page closes or navigates between the down leg and the up leg; renderer crash mid-combo; an up() validation failure for a segment that down() accepted with a different resolution.","commonSituations":"Combos that trigger navigation on key-down (e.g. Enter submitting a form) so the key-up lands on a dead or changed target.","solutions":["Trigger combos on elements that do not navigate mid-combo, or expect and handle the failure","After this error, re-send press() of the same combo on a live page to clear stuck modifiers","Use element.press() which sequences correctly with the page lifecycle","Raise timeouts if the wrapped cause is DeadlineExceeded"],"exampleFix":"// before\nawait page.keyboard.press('Control+Enter'); // Enter submits & navigates, up-leg fails\n\n// after\nawait page.$('#submit').click();           // navigate explicitly\nawait page.keyboard.press('Control+S');    // combos that do not navigate","handlingStrategy":"try-catch","validationCode":"if (page.isClosed()) throw new Error('page closed before combo');\nif (!isValidCombo(combo)) throw new Error(`invalid combo: ${combo}`);","typeGuard":null,"tryCatchPattern":"try {\n  await page.keyboard.press(combo);\n} catch (e) {\n  if (/cannot do key up/.test(e.message) && !page.isClosed()) {\n    await page.keyboard.press(combo); // downs done, ups failed: re-run to clear stuck keys\n  } else throw e;\n}","preventionTips":["Avoid combos whose final key triggers navigation (Enter submitting forms)","Re-press the combo after an up-leg failure to release stuck modifiers","Prefer element.press() which coordinates with the page lifecycle"],"tags":["browser","keyboard","input","combo","stuck-modifiers"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}