Hmbown/CodeWhale · error · ExecError

Start-Process failed

Error message

Start-Process failed: ${r.stderr.trim().slice(0, 200)}

What it means

open_application runs PowerShell's Start-Process to launch the target and throws this ExecError (carrying the process result r) when PowerShell exits non-zero. The message includes up to 200 characters of PowerShell stderr, which names the real cause.

Solutions

  1. Read the embedded stderr slice in the message — it names the missing file or handler
  2. Verify the app is installed and resolvable by bare name (try the full app name as registered, e.g. "Mozilla Firefox")
  3. For URLs, confirm a default handler exists for the scheme (e.g. https opens in some browser)
  4. Catch the ExecError, inspect err's attached process result, and fall back to list_apps to discover valid app names

Example fix

// before
await openApplication({ name: "ffox" });
// after
await openApplication({ name: "firefox" }); // or verify via list_apps first
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await backend.open_application({ name });
} catch (e) {
  if (String(e.message).startsWith("Start-Process failed")) {
    // e carries the process result; log stderr slice from the message
    const apps = await backend.list_apps();
    throw new Error(`Could not launch "${name}". Installed candidates: ${apps.displays ?? JSON.stringify(apps).slice(0, 300)}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Launching a name that isn't an installed app or on PATH (Start-Process cannot find the file); launching a .exe path with spaces mishandled; missing app association for a URL scheme; PowerShell/profile or execution-policy problems.

Common situations: Typos in the executable name; app not installed on the target Windows machine; attempting to open a URL whose protocol handler is unregistered; running in restricted environments where Start-Process is blocked.

Related errors


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

Appendix: source

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

    },
    open_application: async ({ name, bundle_id: bid, url: urlArg, activate } = {}) => {
      const target = name ?? bid;
      if (typeof target !== "string" || !/^[A-Za-z0-9][A-Za-z0-9 .:_-]*$/.test(target)) throw new ExecError("open_application needs a plain app or executable name");
      let argumentsScript = "";
      if (urlArg != null) {
        if (typeof urlArg !== "string" || !URL.canParse(urlArg) || /[\0\r\n]/.test(urlArg)) throw new ExecError("open_application url must be an absolute URL");
        // Start-Process joins ArgumentList into a Windows command line. Quote
        // one argument there, and transport that string as data into PowerShell.
        const quoted = '"' + urlArg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, '$1$1') + '"';
        const encoded = Buffer.from(quoted, "utf16le").toString("base64");
        argumentsScript = `$launchArg = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('${encoded}')); `;
      }
      // activate defaults to background on every platform: a minimized
      // launch leaves the user's foreground window alone. Windows input is
      // still shared-surface — this only controls the launch, not input.
      const windowStyle = activate === true ? "" : " -WindowStyle Minimized";
      const r = await psOk(`${argumentsScript}Start-Process -FilePath "${target}"${windowStyle}${urlArg != null ? " -ArgumentList $launchArg" : ""}; Write-Output '{"launched": true}'`, { timeoutMs: 20_000 });
      if (r.code !== 0) throw new ExecError(`Start-Process failed: ${r.stderr.trim().slice(0, 200)}`, r);
      return { launched: true, name: target, url: urlArg ?? null, activate: activate === true };
    },
    get_app_state: async (args = {}) => {
      if (Object.hasOwn(args, "window_id")) throw unsupportedSelector("Windows get_app_state does not support window_id");
      const { app_ref, detail } = args;
      if (Object.hasOwn(args, "app_ref") && (!app_ref || typeof app_ref !== "object" || Array.isArray(app_ref)
        || Object.keys(app_ref).length !== 1 || !Object.hasOwn(app_ref, "name") || typeof app_ref.name !== "string" || !app_ref.name.trim())) {
        throw unsupportedSelector("Windows get_app_state supports only app_ref: { name: exact window title }; PID, bundle_id and other references are unsupported");
      }
      const filter = Buffer.from(app_ref?.name ?? "", "utf16le").toString("base64");
      const maxEls = detail === "full" ? 800 : 400;
      const j = await psJson(`${UIA_PRELUDE}
$max = ${maxEls};
$filter = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('${filter}'));
$root = [System.Windows.Automation.AutomationElement]::RootElement;
$els = New-Object System.Collections.ArrayList;
$found = $false; $truncated = $false; $appName = $null;
$targets = @($root.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition));

View on GitHub (pinned to 73e0f67d83)