{"record":{"id":"2b98deb2a7993584","repo":"Hmbown/CodeWhale","slug":"no-non-modifier-key-in-text","errorCode":null,"errorMessage":"no non-modifier key in \"${text}\"","messagePattern":"no non-modifier key in \"(.+?)\"","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":495,"sourceCode":"      // The acknowledgement carries the yield_ms the helper waited for a\n      // hardware-input gap before posting the press.\n      return lease.receipt;\n    } finally {\n      await withSignal(null, () => lease.release());\n    }\n  }\n\n  function parseChord(text) {\n    const parts = String(text).split(\"+\").map((s) => s.trim().toLowerCase()).filter(Boolean);\n    if (!parts.length) throw new ExecError(\"empty key text\");\n    let flags = 0;\n    let key = null;\n    for (const p of parts) {\n      if (MODIFIERS[p] != null) flags |= MODIFIERS[p];\n      else if (KEY_CODES[p] != null) { if (key) throw new ExecError(`multiple non-modifier keys in \"${text}\"`); key = p; }\n      else throw new ExecError(`unknown key \"${p}\" (supported: ${Object.keys(KEY_CODES).join(\", \")} + modifiers cmd/ctrl/alt/shift/fn)`);\n    }\n    if (key == null) throw new ExecError(`no non-modifier key in \"${text}\"`);\n    return { flags, code: KEY_CODES[key], key };\n  }\n\n  // ---------- displays ----------\n  async function displayInfo() { return native(\"displays\"); }\n\n  // ---------- screenshots ----------\n  function recordingsDir() {\n    return process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), \".codewhale-cu\", \"recordings\");\n  }\n\n  async function screenshot({ display, region, app_ref, window_id, path: outPath } = {}) {\n    // Once an app is selected, ordinary observations follow it behind the\n    // user's work. An explicit display/region remains a deliberate desktop capture.\n    if (app_ref === undefined && display === undefined && region === undefined) app_ref = state.inputApp ?? undefined;\n    const dir = recordingsDir();\n    fs.mkdirSync(dir, { recursive: true });\n    // JPEG, not PNG. A screen is photographic content — gradients, wallpaper,","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L477-L513","documentation":"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.","triggerScenarios":"Passing key='cmd', 'shift+alt', 'cmd+ctrl' — modifier tokens only, no KEY_CODES entry.","commonSituations":"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.","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"],"exampleFix":"// before\nawait press(\"cmd+shift\");\n// after\nawait press(\"cmd+shift+p\");","handlingStrategy":"validation","validationCode":"const MODS = new Set([\"cmd\", \"ctrl\", \"alt\", \"shift\", \"fn\"]);\nconst parts = key.split(\"+\").map(s => s.trim().toLowerCase()).filter(Boolean);\nif (parts.every(p => MODS.has(p))) throw new Error(\"chord must include a non-modifier key\");","typeGuard":"const hasMainKey = (k) => k.split(\"+\").some(p => ![\"cmd\",\"ctrl\",\"alt\",\"shift\",\"fn\"].includes(p.trim().toLowerCase()));","tryCatchPattern":"try {\n  await press(key);\n} catch (e) {\n  if (e instanceof ExecError && e.message.startsWith(\"no non-modifier key\")) {\n    // reject or ask the caller for the key to combine with the modifiers\n  } else throw e;\n}","preventionTips":["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"],"tags":["keyboard","chord","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}