moeru-ai/airi · error · Error
Invalid accelerator " ": multiple non-modifier keys (" ", "…
Error message
Invalid accelerator "${input}": multiple non-modifier keys ("${key}", "${token}") What it means
Thrown by parseAccelerator when more than one non-modifier key token is present. An accelerator must end with exactly one main key; the parser records the first non-modifier token in `key` and throws as soon as a second one appears. Input like 'Ctrl+A+B' or 'Shift+F1+F2' is rejected.
Solutions
- Split multi-key chords into separate accelerators and handle sequencing yourself; each accelerator string keeps exactly one key token
- If concatenating strings, ensure only the final segment is a key — treat earlier segments as modifiers only
- Validate with isValidAccelerator(input) before saving user bindings
- In capture UI, reset the recorded key slot on each new non-modifier keydown
Example fix
// before
parseAccelerator('Ctrl+K Ctrl+S') // second token after space also fails
parseAccelerator('Ctrl+K+S') // throws: multiple non-modifier keys
// after
// model chords as an ordered list of accelerators
const chord = [parseAccelerator('Ctrl+K'), parseAccelerator('Ctrl+S')] Defensive patterns
Strategy: validation
Validate before calling
import { isValidAccelerator } from '../global-shortcut/accelerators'
if (!isValidAccelerator(candidate)) {
// reject or treat as chord sequence, e.g. split on space and validate each part
const parts = candidate.split(/\s+/)
if (!parts.every(isValidAccelerator)) return null
} Try / catch
catch (e) { if (e instanceof Error && e.message.includes('multiple non-modifier keys')) return splitIntoChord(input); throw e } Prevention
- Model key chords as arrays of single-key accelerators, never one joined string
- Reset the captured key slot on every new non-modifier keydown
- Never append a second key token to an existing binding string
When it happens
Trigger: Calling parseAccelerator('Ctrl+A+B'), parseAccelerator('A B'), or passing a chord/sequence ('Ctrl+K Ctrl+S' style) where two key tokens end up joined with '+' instead of separated as a sequence.
Common situations: Trying to express VS Code-style key chords in a single accelerator string; concatenating a previously parsed key back onto a new binding ('Ctrl+' + 'A' + 'B'); capture UI that records two taps of letter keys without clearing the key slot between them.
Related errors
- Invalid accelerator " ": duplicate modifier
- Invalid accelerator " ": empty token
- Invalid accelerator " ": no key token
- Invalid accelerator: empty string
- Invalid accelerator: unknown key
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/eae784308a09ffc5.
Report an issue: GitHub.
Appendix: source
Thrown at packages/stage-shared/src/global-shortcut/accelerators.ts:335
const rawTokens = trimmed.split('+')
const modifiers: ShortcutModifier[] = []
let key: ShortcutKey | undefined
for (const raw of rawTokens) {
const token = raw.trim()
if (token.length === 0)
throw new Error(`Invalid accelerator "${input}": empty token`)
const modifier = lookupModifierToken(token)
if (modifier !== undefined) {
if (modifiers.includes(modifier))
throw new Error(`Invalid accelerator "${input}": duplicate modifier "${modifier}"`)
modifiers.push(modifier)
continue
}
if (key !== undefined)
throw new Error(`Invalid accelerator "${input}": multiple non-modifier keys ("${key}", "${token}")`)
key = normalizeKeyToken(token)
}
if (key === undefined)
throw new Error(`Invalid accelerator "${input}": no key token`)
return { modifiers, key }
}
/**
* Tests whether `input` is a well-formed accelerator string.
*
* Use when:
* - Gating user input without needing the parsed result
*
* Returns:
* - `true` when `parseAccelerator` would succeed, `false` otherwise
*/View on GitHub (pinned to 677329427f)