grafana/k6 · error

sending key up: %w

Error message

sending key up: %w

What it means

Top-level wrapper returned by Keyboard.Up when the private k.up() fails. Like Down, it covers either an invalid key name for the active layout or a failed CDP Input.dispatchKeyEvent(KeyUp) call. Note the modifier bit and pressed-key bookkeeping happen before dispatch, so a failure here can leave keyboard modifier state inconsistent in the session.

Source

Thrown at internal/js/modules/k6/browser/common/keyboard.go:64

		session:     s,
		pressedKeys: make(map[int64]struct{}),
		layoutName:  "us",
		layout:      keyboardlayout.GetKeyboardLayout("us"),
	}
}

// Down sends a key down message to a session target.
func (k *Keyboard) Down(key string) error {
	if err := k.down(key); err != nil {
		return fmt.Errorf("sending key down: %w", err)
	}
	return nil
}

// Up sends a key up message to a session target.
func (k *Keyboard) Up(key string) error {
	if err := k.up(key); err != nil {
		return fmt.Errorf("sending key up: %w", err)
	}
	return nil
}

// Press sends a key press message to a session target.
// It delays the action if `Delay` option is specified.
// A press message consists of successive key down and up messages.
func (k *Keyboard) Press(key string, kbdOpts KeyboardOptions) error {
	if err := k.comboPress(key, kbdOpts); err != nil {
		return fmt.Errorf("pressing key: %w", err)
	}

	return nil
}

// InsertText inserts a text without dispatching key events.
func (k *Keyboard) InsertText(text string) error {
	if err := k.insertText(text); err != nil {

View on GitHub (pinned to 93accf6570)

Solutions

  1. Use valid US-layout key names ('Control', 'Shift', 'Enter')
  2. Keep down()/up() pairs adjacent with no navigation or page close between them
  3. Prefer press('Control+A') which handles the down/up pair atomically via comboPress
  4. Verify the page is open before the call

Example fix

// before
await page.keyboard.down('Control');
await page.click('#link');     // may navigate/close
await page.keyboard.up('Control');

// after
await page.keyboard.press('Control+A');  // atomic down+up
Defensive patterns

Strategy: validation

Validate before calling

if (page.isClosed()) throw new Error('page closed before key up');
// validate key name against US-layout list (see isValidKey) before up()

Type guard

function isValidKey(k) {
  const VALID = new Set(['Enter','Backspace','Tab','Escape','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Home','End','PageUp','PageDown','Control','Shift','Alt','Meta']);
  return VALID.has(k) || /^[a-zA-Z0-9]$/.test(k) || /^F([1-9]|1[0-2])$/.test(k);
}

Try / catch

try {
  await page.keyboard.up(key);
} catch (e) {
  if (/not a valid key/.test(e.message)) throw new Error(`bad key name: ${key}`);
  if (/dispatching key event up/.test(e.message)) await page.keyboard.press(key); // resync modifiers
  else throw e;
}

Prevention

When it happens

Trigger: keyboard.up('Control') with a key name not in the layout; calling up() after the CDP session died (page closed, browser crashed); unbalanced down/up sequences where the second call races a navigation.

Common situations: Calling down() and up() around an await that navigates or closes the page; using localized key names from non-US keyboards.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/430824204cfb978c. Report an issue: GitHub.