{"record":{"id":"690a21e08a2ec0c0","repo":"Hmbown/CodeWhale","slug":"unicode-input-failed-failure-message-key-up-recovery-failed","errorCode":null,"errorMessage":"Unicode input failed: \" + failure.Message + \"; key-up recovery failed: \" + cleanup.Message","messagePattern":"Unicode input failed: \" \\+ failure\\.Message \\+ \"; key-up recovery failed: \" \\+ cleanup\\.Message","errorType":"exception","errorClass":"AggregateException","httpStatus":null,"severity":"critical","filePath":"crates/tui/plugins/computer-use/src/backends/win32.mjs","lineNumber":63,"sourceCode":"      throw new InputDeliveryException(sent, inputs.Length, error);\n    }\n  }\n  public static void SendKey(ushort vk, uint flags) {\n    SendChecked(new INPUT[] { KeyInput(vk, 0, flags) });\n  }\n  public static void SendString(string text) {\n    // UTF-16 code units, including both halves of surrogate pairs. UNICODE\n    // requires wVk=0 and may only be combined with KEYUP, never SCANCODE.\n    foreach (char c in text) {\n      INPUT up = KeyInput(0, c, UNICODE | KEYUP);\n      try { SendChecked(new INPUT[] { KeyInput(0, c, UNICODE), up }); }\n      catch (InputDeliveryException failure) {\n        // A partial pair may leave only our down event inserted. Release it\n        // once, never replay text, and still report the original failure.\n        if (failure.Inserted == 1) {\n          try { SendChecked(new INPUT[] { up }); }\n          catch (Exception cleanup) {\n            throw new AggregateException(\"Unicode input failed: \" + failure.Message + \"; key-up recovery failed: \" + cleanup.Message, failure, cleanup);\n          }\n        }\n        throw;\n      }\n    }\n  }\n}`;\n\nfunction recordingsDir() {\n  return process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(os.homedir(), \".codewhale-cu\", \"recordings\");\n}\n\n// Windows virtual-key codes for named keys.\nconst VK = {\n  return: 0x0d, enter: 0x0d, tab: 0x09, escape: 0x1b, esc: 0x1b, space: 0x20,\n  backspace: 0x08, delete: 0x2e, home: 0x24, end: 0x23, pageup: 0x21, pagedown: 0x22,\n  left: 0x25, up: 0x26, right: 0x27, down: 0x28, capslock: 0x14, insert: 0x2d,\n  f1: 0x70, f2: 0x71, f3: 0x72, f4: 0x73, f5: 0x74, f6: 0x75, f7: 0x76, f8: 0x77,","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/win32.mjs#L45-L81","documentation":"When injecting a Unicode character (KEYEVENTF_UNICODE down/up pair), the backend catches InputDeliveryException from the SendChecked of the pair. If exactly one event was inserted (only the key-down), it attempts to release that key once with a key-up; if that recovery also fails, it throws an AggregateException combining both messages, then otherwise rethrows the original failure. This prevents leaving a stuck virtual key after partial injection.","triggerScenarios":"SendString of a Unicode character when the desktop is locked/secure, UIPI blocks injection after the first event, or input is disabled mid-send — the down lands but the up does not, and the recovery up also fails.","commonSituations":"Text automation racing a screen lock or UAC elevation prompt; sending to an elevated app from a non-elevated process; RDP disconnecting between the pair's two events.","solutions":["Read both inner exceptions: the original injection failure and the key-up cleanup failure, and fix the underlying cause (usually elevation/UIPI per error 284)","Ensure the session stays unlocked and elevated consistently for the duration of text input","Wrap text sending in try/catch and, on failure, send a harmless key (e.g. VK_SHIFT up) or reset keyboard state to clear any stuck key","Retry the whole SendString after the environment stabilizes; never retry only the character that half-landed without clearing state"],"exampleFix":"// before\nSendString(text);\n// after\ntry { SendString(text); }\ncatch (AggregateException e) {\n  // e.InnerExceptions[0] = original SendInput failure, [1] = key-up recovery failure\n  ClearKeyboardState();\n  throw;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { SendString(text); }\ncatch (AggregateException e) {\n  foreach (var ex in e.InnerExceptions) Log(ex.Message);\n  ClearKeyboardState(); // release any stuck key\n  throw;\n}\ncatch (InputDeliveryException e) { Log(e.Message); throw; }","preventionTips":["Never leave a half-injected Unicode pair without a key-up recovery","Suspend screen-lock/UAC prompts during automated typing","Reset keyboard state after any failed injection batch","Send long text in chunks so failures are recoverable"],"tags":["win32","unicode","sendinput","aggregate-exception","stuck-key"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}