can1357/oh-my-pi · error
unterminated quoted key literal
Error message
unterminated quoted key literal
What it means
parseKeyTokens() supports quoted literals (e.g. "'abc'") that expand to per-character key events, with backslash escapes. If the closing quote never appears before end of input, the parser throws this error.
Source
Thrown at packages/tui/src/debug-server.ts:169
return alt ? `\x1b${character}` : character;
}
function parseKeyTokens(source: string): { sequences: string[]; events: number } {
let offset = 0;
const sequences: string[] = [];
let events = 0;
while (offset < source.length) {
while (/\s/.test(source[offset] ?? "")) offset++;
if (offset >= source.length) break;
const quote = source[offset];
if (quote === "'" || quote === '"') {
offset++;
let literal = "";
while (offset < source.length && source[offset] !== quote) {
if (source[offset] === "\\" && offset + 1 < source.length) offset++;
literal += source[offset++];
}
if (source[offset] !== quote) throw new Error("unterminated quoted key literal");
offset++;
sequences.push(...Array.from(literal));
events += Array.from(literal).length;
continue;
}
const start = offset;
while (offset < source.length && !/\s/.test(source[offset] ?? "")) offset++;
sequences.push(encodeChord(source.slice(start, offset)));
events++;
}
return { sequences, events };
}
function mouseSequence(x: number, y: number, action: string): string {
if (!Number.isInteger(x) || !Number.isInteger(y) || x < 0 || y < 0)
throw new Error("mouse coordinates must be non-negative integers");
const at = (button: number, release = false): string => `\x1b[<${button};${x + 1};${y + 1}${release ? "m" : "M"}`;
switch (action) {View on GitHub (pinned to 9690622007)
Solutions
- Close the quoted literal: "C-a,'hello'"
- Escape internal quotes with a backslash
- Check shell escaping if the sequence is passed via CLI — use single-quoted shell args or escape appropriately
Example fix
// before
parseKeyTokens("'hello") // unterminated
// after
parseKeyTokens("'hello'") Defensive patterns
Strategy: validation
Validate before calling
function quotesBalanced(s: string): boolean {
let i = 0;
while (i < s.length) {
const q = s[i];
if (q === "'" || q === '"') {
i++;
while (i < s.length && s[i] !== q) { if (s[i] === "\\") i++; i++; }
if (i >= s.length) return false;
}
i++;
}
return true;
} Try / catch
try {
tokens = parseKeyTokens(seq);
} catch (err) {
if (err instanceof Error && err.message === "unterminated quoted key literal") {
logger.warn("unterminated quote in key sequence", { seq });
tokens = parseKeyTokens(`${seq}'`); // best-effort close
} else throw err;
} Prevention
- Always close quoted literals in key sequences
- Escape literal quotes/backslashes with backslash
- Be careful with shell quoting when passing sequences via CLI
When it happens
Trigger: Passing a key sequence containing an opening quote (' or ") without the matching close — e.g. "C-a,'hello".
Common situations: Shell quoting stripping the closing quote; hand-written sequences with an apostrophe intended as a literal but starting a quote; truncation of the sequence string.
Related errors
- invalid key token ${token}
- unknown key ${rest}
- Replacement text is not valid UTF-8: {err}
- V2 compaction stream parse failed: ${err instanceof Error ?
- ${source} response is missing credentials.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1fc7cc863af17eb3.
Report an issue: GitHub.