{"record":{"id":"0a9ac9cf927f7aa7","repo":"microsoft/playwright","slug":"unknown-modifier-modifier","errorCode":null,"errorMessage":"Unknown modifier ${modifier}","messagePattern":"Unknown modifier (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/input.ts","lineNumber":161,"sourceCode":"    }\n\n    const tokens = split(key);\n    key = tokens[tokens.length - 1];\n    for (let i = 0; i < tokens.length - 1; ++i)\n      await this.down(progress, tokens[i]);\n    await this.down(progress, key);\n    if (options.delay)\n      await progress.wait(options.delay);\n    await this.up(progress, key);\n    for (let i = tokens.length - 2; i >= 0; --i)\n      await this.up(progress, tokens[i]);\n  }\n\n  async ensureModifiers(progress: Progress, mm: types.SmartKeyboardModifier[]): Promise<types.KeyboardModifier[]> {\n    const modifiers = mm.map(resolveSmartModifier);\n    for (const modifier of modifiers) {\n      if (!kModifiers.includes(modifier))\n        throw new Error('Unknown modifier ' + modifier);\n    }\n    const restore: types.KeyboardModifier[] = Array.from(this._pressedModifiers);\n    for (const key of kModifiers) {\n      const needDown = modifiers.includes(key);\n      const isDown = this._pressedModifiers.has(key);\n      if (needDown && !isDown)\n        await this.down(progress, key);\n      else if (!needDown && isDown)\n        await this.up(progress, key);\n    }\n    return restore;\n  }\n\n  _modifiers(): Set<types.KeyboardModifier> {\n    return this._pressedModifiers;\n  }\n}\n","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/input.ts#L143-L179","documentation":"Thrown by Keyboard.ensureModifiers() when a resolved modifier is not in the kModifiers set ['Alt', 'Control', 'Meta', 'Shift']. The method maps each input through resolveSmartModifier (which handles ControlOrMeta) and then validates against the four allowed modifier keys. Any modifier outside this set is rejected.","triggerScenarios":"Calling internal keyboard APIs that use ensureModifiers with an invalid modifier name. In normal usage this is rare since the public API only accepts valid modifiers, but custom selector engines or internal tool integrations may pass unexpected values.","commonSituations":"Passing 'Cmd' (should be 'Meta'), 'Option' (should be 'Alt'), 'Ctrl' (should be 'Control'), or 'Win' (should be 'Meta'). Custom automation code that constructs modifier arrays dynamically. Integration with third-party tools that use platform-specific modifier names.","solutions":["Use only the four canonical modifier names: 'Alt', 'Control', 'Meta', 'Shift'.","Use 'ControlOrMeta' for cross-platform modifier (resolves to Meta on macOS, Control on other platforms).","Map platform-specific names before passing: { Cmd: 'Meta', Option: 'Alt', Ctrl: 'Control' }."],"exampleFix":"// before\nawait keyboard.ensureModifiers(['Cmd', 'Shift']);\n\n// after\nawait keyboard.ensureModifiers(['Meta', 'Shift']);","handlingStrategy":"validation","validationCode":"const VALID_MODIFIERS = new Set(['Alt','Control','Meta','Shift','ControlOrMeta']);\nfunction validateModifiers(modifiers) {\n  for (const m of modifiers)\n    if (!VALID_MODIFIERS.has(m))\n      throw new Error(`Unknown modifier: ${m}`);\n}","typeGuard":"function isModifier(key: string): key is 'Alt' | 'Control' | 'Meta' | 'Shift' | 'ControlOrMeta' {\n  return ['Alt','Control','Meta','Shift','ControlOrMeta'].includes(key);\n}","tryCatchPattern":null,"preventionTips":["Use only 'Alt', 'Control', 'Meta', 'Shift', or 'ControlOrMeta'.","Map platform-specific names: Cmd->Meta, Option->Alt, Ctrl->Control.","Use TypeScript literal union types for modifier parameters."],"tags":["keyboard","modifier","input","validation"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}