Hmbown/CodeWhale · error · ExecError
no non-modifier key in
Error message
no non-modifier key in "${text}" What it means
After parsing all '+'-separated tokens, parseChord requires at least one non-modifier key. A chord consisting only of modifiers (e.g. 'cmd+shift') names no key to press, so it is rejected instead of sending a modifier-only event.
Solutions
- Append the actual key to press: 'cmd+c', not 'cmd'
- If the goal is holding modifiers, use dedicated press/release or modifier-hold APIs if the backend exposes them
- Fix the chord-building code that failed to include the main key
Example fix
// before
await press("cmd+shift");
// after
await press("cmd+shift+p"); Defensive patterns
Strategy: validation
Validate before calling
const MODS = new Set(["cmd", "ctrl", "alt", "shift", "fn"]);
const parts = key.split("+").map(s => s.trim().toLowerCase()).filter(Boolean);
if (parts.every(p => MODS.has(p))) throw new Error("chord must include a non-modifier key"); Type guard
const hasMainKey = (k) => k.split("+").some(p => !["cmd","ctrl","alt","shift","fn"].includes(p.trim().toLowerCase())); Try / catch
try {
await press(key);
} catch (e) {
if (e instanceof ExecError && e.message.startsWith("no non-modifier key")) {
// reject or ask the caller for the key to combine with the modifiers
} else throw e;
} Prevention
- Require at least one non-modifier token in chord builders
- Do not use this API to 'hold' modifiers; use a dedicated modifier API if one exists
- Unit-test chord builders for modifier-only inputs
When it happens
Trigger: Passing key='cmd', 'shift+alt', 'cmd+ctrl' — modifier tokens only, no KEY_CODES entry.
Common situations: Caller wants to 'hold' modifiers (unsupported via this API) or mistakenly believes modifiers alone trigger an action; a chord builder that appends modifiers but drops the main key.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- multiple non-modifier keys in
- empty key text
- unknown key combination
- unknown key " " (supported: + modifiers…
- 1
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2b98deb2a7993584.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:495
// The acknowledgement carries the yield_ms the helper waited for a
// hardware-input gap before posting the press.
return lease.receipt;
} finally {
await withSignal(null, () => lease.release());
}
}
function parseChord(text) {
const parts = String(text).split("+").map((s) => s.trim().toLowerCase()).filter(Boolean);
if (!parts.length) throw new ExecError("empty key text");
let flags = 0;
let key = null;
for (const p of parts) {
if (MODIFIERS[p] != null) flags |= MODIFIERS[p];
else if (KEY_CODES[p] != null) { if (key) throw new ExecError(`multiple non-modifier keys in "${text}"`); key = p; }
else throw new ExecError(`unknown key "${p}" (supported: ${Object.keys(KEY_CODES).join(", ")} + modifiers cmd/ctrl/alt/shift/fn)`);
}
if (key == null) throw new ExecError(`no non-modifier key in "${text}"`);
return { flags, code: KEY_CODES[key], key };
}
// ---------- displays ----------
async function displayInfo() { return native("displays"); }
// ---------- screenshots ----------
function recordingsDir() {
return process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), ".codewhale-cu", "recordings");
}
async function screenshot({ display, region, app_ref, window_id, path: outPath } = {}) {
// Once an app is selected, ordinary observations follow it behind the
// user's work. An explicit display/region remains a deliberate desktop capture.
if (app_ref === undefined && display === undefined && region === undefined) app_ref = state.inputApp ?? undefined;
const dir = recordingsDir();
fs.mkdirSync(dir, { recursive: true });
// JPEG, not PNG. A screen is photographic content — gradients, wallpaper,View on GitHub (pinned to 73e0f67d83)