{"record":{"id":"e2b149cbfcdc1021","repo":"moeru-ai/airi","slug":"invalid-accelerator-input-duplicate-modifier","errorCode":null,"errorMessage":"Invalid accelerator \"${input}\": duplicate modifier \"${modifier}\"","messagePattern":"Invalid accelerator \"(.+?)\": duplicate modifier \"(.+?)\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-shared/src/global-shortcut/accelerators.ts","lineNumber":329,"sourceCode":" */\nexport function parseAccelerator(input: string): ShortcutAccelerator {\n  const trimmed = input.trim()\n  if (trimmed.length === 0)\n    throw new Error('Invalid accelerator: empty string')\n\n  const rawTokens = trimmed.split('+')\n  const modifiers: ShortcutModifier[] = []\n  let key: ShortcutKey | undefined\n\n  for (const raw of rawTokens) {\n    const token = raw.trim()\n    if (token.length === 0)\n      throw new Error(`Invalid accelerator \"${input}\": empty token`)\n\n    const modifier = lookupModifierToken(token)\n    if (modifier !== undefined) {\n      if (modifiers.includes(modifier))\n        throw new Error(`Invalid accelerator \"${input}\": duplicate modifier \"${modifier}\"`)\n      modifiers.push(modifier)\n      continue\n    }\n\n    if (key !== undefined)\n      throw new Error(`Invalid accelerator \"${input}\": multiple non-modifier keys (\"${key}\", \"${token}\")`)\n    key = normalizeKeyToken(token)\n  }\n\n  if (key === undefined)\n    throw new Error(`Invalid accelerator \"${input}\": no key token`)\n\n  return { modifiers, key }\n}\n\n/**\n * Tests whether `input` is a well-formed accelerator string.\n *","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-shared/src/global-shortcut/accelerators.ts#L311-L347","documentation":"Thrown by parseAccelerator when the same modifier appears twice in one accelerator string. lookupModifierToken maps spelling variants (e.g. 'Ctrl'/'Control', 'Cmd'/'Command', 'CommandOrControl') onto one canonical ShortcutModifier, and the parser rejects repeats of the same canonical modifier even if the spelling differs. Input like 'Ctrl+Control+A' or 'Ctrl+Cmd+Ctrl+K' is therefore invalid.","triggerScenarios":"Calling parseAccelerator('Ctrl+Ctrl+A'), parseAccelerator('CommandOrControl+Ctrl+X') when both tokens resolve to the same modifier entry, or duplicating a modifier when merging two partial keybinding configurations.","commonSituations":"Merging a default modifier set with user overrides and concatenating them ('Ctrl' + 'Ctrl+A'); key-capture UI that records a held modifier twice on key repeat; copy-pasting a binding from another OS (e.g. keeping both 'Cmd' and 'CommandOrControl').","solutions":["Deduplicate modifier tokens before joining: [...new Set(tokens)].join('+')","When capturing from real keyboard events, accumulate modifiers into a Set keyed by the canonical modifier, not a list of raw tokens","Validate with isValidAccelerator(input) before storing or parsing","When merging configs, let user modifiers replace rather than append to defaults"],"exampleFix":"// before\nconst binding = `${defaultMods}+${userMods}+${key}` // may yield 'Ctrl+Ctrl+K'\nconst acc = parseAccelerator(binding)\n\n// after\nconst tokens = [...new Set([...defaultMods.split('+'), ...userMods.split('+')])]\nconst binding = `${tokens.join('+')}+${key}`\nconst acc = parseAccelerator(binding)","handlingStrategy":"validation","validationCode":"import { isValidAccelerator } from '../global-shortcut/accelerators'\n\nconst binding = [...new Set(rawTokens)].join('+')\nif (!isValidAccelerator(binding)) return defaultBinding\nconst acc = parseAccelerator(binding)","typeGuard":null,"tryCatchPattern":"catch (e) { if (e instanceof Error && e.message.includes('duplicate modifier')) return promptUserToRebind(); throw e }","preventionTips":["Accumulate modifiers in a Set keyed by canonical modifier during key capture","Let user modifier overrides replace defaults instead of concatenating","Run settings-load validation and fall back to defaults for invalid entries"],"tags":["keyboard-shortcut","accelerator","duplicate-modifier","input-validation"],"backgroundTag":"invalid-accelerator-string","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}