{"record":{"id":"85686faf46f47451","repo":"can1357/oh-my-pi","slug":"invalid-key-token-token","errorCode":null,"errorMessage":"invalid key token ${token}","messagePattern":"invalid key token (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tui/src/debug-server.ts","lineNumber":135,"sourceCode":"\nfunction encodeChord(token: string): string {\n\tlet rest = token;\n\tlet ctrl = false;\n\tlet alt = false;\n\tlet shift = false;\n\tif (/^C-/i.test(rest)) {\n\t\tctrl = true;\n\t\trest = rest.slice(2);\n\t}\n\tif (/^(?:A|M)-/i.test(rest)) {\n\t\talt = true;\n\t\trest = rest.slice(2);\n\t}\n\tif (/^S-/i.test(rest)) {\n\t\tshift = true;\n\t\trest = rest.slice(2);\n\t}\n\tif (rest.length === 0 || /^(?:C-|A-|M-|S-)/i.test(rest)) throw new Error(`invalid key token ${token}`);\n\n\tconst name = rest.toLowerCase();\n\tconst special = SPECIAL_KEYS[name];\n\tif (special !== undefined) {\n\t\tif (ctrl && name === \"space\") return alt ? \"\\x1b\\x00\" : \"\\x00\";\n\t\tif (ctrl && name === \"backspace\") return alt ? \"\\x1b\\x08\" : \"\\x08\";\n\t\tconst modifier = 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0);\n\t\tlet encoded = modifiedSpecial(special, modifier);\n\t\tif (alt && encoded === special) encoded = `\\x1b${encoded}`;\n\t\treturn encoded;\n\t}\n\tif (Array.from(rest).length !== 1) throw new Error(`unknown key ${rest}`);\n\tlet character = rest;\n\tif (shift && /^[a-z]$/i.test(character)) character = character.toUpperCase();\n\tif (ctrl) character = ctrlCharacter(character);\n\treturn alt ? `\\x1b${character}` : character;\n}\n","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/tui/src/debug-server.ts#L117-L153","documentation":"encodeChord() parses key tokens with optional C-/A-/M-/S- modifier prefixes. If after stripping modifiers nothing remains, or another modifier prefix is dangling (e.g. \"C-\" with no key), the token is malformed and this error is thrown.","triggerScenarios":"Passing tokens like \"\", \"C-\", \"A-\", \"C-A-S-\" (empty base key), or a token where only modifier prefixes remain to the debug server key parser.","commonSituations":"String-splitting bugs leaving empty tokens (e.g. \"a,,b\" or trailing \"+\"); building key sequences programmatically with a trailing separator; typo'd sequences like \"C--\" intent.","solutions":["Ensure each token has a non-empty base key after modifiers: \"C-a\", not \"C-\"","Fix splitting logic so empty/whitespace tokens are filtered out","Use single-letter or SPECIAL_KEYS names as the base key"],"exampleFix":"// before\nconst keys = \"C-a,S-\".split(\",\").map(encodeChord) // throws on \"S-\"\n// after\nconst keys = \"C-a,S-b\".split(\",\").map(encodeChord)","handlingStrategy":"validation","validationCode":"function isValidKeyToken(token: string): boolean {\n  const rest = token.replace(/^(?:[CAM]-)+/i, \"\");\n  return rest.length > 0 && !/^(?:C-|A-|M-|S-)/i.test(rest) &&\n    (rest.length === 1 || rest.toLowerCase() in SPECIAL_KEYS);\n}","typeGuard":null,"tryCatchPattern":"try {\n  tokens = parseKeyTokens(input);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"invalid key token\")) {\n    logger.warn(\"malformed key token in sequence\", { input });\n    tokens = [];\n  } else throw err;\n}","preventionTips":["Filter out empty tokens after splitting sequences on separators","Never end modifier prefixes without a base key (\"C-\" alone is invalid)","Validate the whole sequence with the parser in tests before sending at runtime"],"tags":["keyboard-input","parsing","debug"],"backgroundTag":"invalid-key-sequence","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}