Hmbown/CodeWhale · error · ExecError

powershell.exe exited

Error message

powershell.exe exited ${r.code}: ${(messages.join("") || raw).slice(0, 1600)}

What it means

After a PowerShell invocation finishes with a nonzero exit code, psOk extracts the CLIXML <S S="Error"> messages (decoding _xHHHH_ escapes and XML entities) from stderr/stdout and throws this ExecError containing the exit code plus the decoded error text (truncated to 1600 chars). It surfaces the real PowerShell error strings rather than raw CLIXML noise.

Solutions

  1. Read the decoded error text in the message — it contains the actual PowerShell exception
  2. Run the same script manually with `-EncodedCommand` (or paste it) to reproduce outside the harness
  3. Add try/catch inside your PowerShell script and emit structured error JSON instead of a bare throw
  4. Verify required modules/permissions (run elevated if UIA targets elevated windows)

Example fix

// before
const r = await psOk(script);
// after
try { const r = await psOk(script); }
catch (e) { if (/powershell\.exe exited/.test(e.message)) console.error('PS failure detail:', e.message); throw e; }
Defensive patterns

Strategy: try-catch

Try / catch

try { return await psOk(script); }
catch (e) {
  const detail = /powershell\.exe exited \d+: ([\s\S]*)/.exec(e.message)?.[1] ?? '';
  log.error('PowerShell failed', detail.slice(0, 300));
  throw e;
}

Prevention

When it happens

Trigger: Running any psOk-backed script that throws a terminating error, hits a missing cmdlet/module, fails a P/Invoke call, or exits with a nonzero code (e.g. script uses `exit 1`, or a UIA COM call fails).

Common situations: Typo'd cmdlet or missing module (CommandNotFoundException); access denied talking to UIA or privileged windows; encoding corruption making the message garbled; scripts that `exit 1` to signal business-logic failures.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/970656a3d5c397fc. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/win32.mjs:190

    return runner("powershell.exe", ["-NoProfile", "-NonInteractive", "-EncodedCommand", encoded], {
      timeoutMs: o.timeoutMs ?? 25_000,
      maxBuffer: 32 * 1024 * 1024,
    });
  }

  /** ps() but truthful: timeout, nonzero exit, and spawn failure all throw. */
  async function psOk(script, o = {}) {
    const r = await ps(script, o);
    if (r.aborted) throw Object.assign(new ExecError("computer request cancelled", r), { code: "cancelled" });
    if (r.timedOut) throw new ExecError(`powershell timed out after ${o.timeoutMs ?? 25_000}ms`, r);
    if (r.code !== 0) {
      const raw = (r.stderr || r.stdout).trim();
      // EncodedCommand serializes errors as CLIXML; surface the error strings,
      // not a truncated XML/progress header that conceals the actual failure.
      const messages = [...raw.matchAll(/<S S="Error">([\s\S]*?)<\/S>/g)].map(m => m[1]
        .replace(/_x([0-9A-Fa-f]{4})_/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
        .replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&"));
      throw new ExecError(`powershell.exe exited ${r.code}: ${(messages.join("") || raw).slice(0, 1600)}`, r);
    }
    return r;
  }

  async function psJson(script, o = {}) {
    const r = await psOk(script, o);
    const out = r.stdout.trim();
    const j = tryJson(out, null);
    if (!j) throw new ExecError(`powershell did not return JSON: ${(r.stderr || out).trim().slice(0, 300)}`, r);
    return j;
  }

  let lastRaster = null;
  let activeDisplay = null;
  const heldButtons = new Set();
  const heldKeys = new Set();

  async function releaseInput({ buttons = [...heldButtons], keys = [...heldKeys] } = {}) {

View on GitHub (pinned to 73e0f67d83)