Hmbown/CodeWhale · error · ExecError

output path must be an absolute filename

Error message

output path must be an absolute filename

What it means

`outputPath` validates that every file the Linux backend writes (screenshots, recordings) is an absolute path string containing no NUL byte. Relative paths and non-string inputs are rejected so files land in a deterministic location regardless of the backend process's working directory.

Solutions

  1. Resolve the path to absolute before calling: `path.resolve("shot.png")`.
  2. Use path.join(os.homedir(), ...) or another absolute base directory.
  3. Validate the input is a non-empty string before passing it.

Example fix

// before
await backend.screenshot({ file: "shot.png" }); // throws
// after
import path from "node:path";
await backend.screenshot({ file: path.resolve("shot.png") });
Defensive patterns

Strategy: validation

Validate before calling

function assertAbsoluteOut(p) {
  if (typeof p !== "string" || !path.isAbsolute(p) || p.includes("\0"))
    throw new TypeError("output path must be an absolute filename");
}

Type guard

const isSafeAbsPath = (p) => typeof p === "string" && path.isAbsolute(p) && !p.includes("\0");

Try / catch

try { await backend.screenshot({ file }); }
catch (e) {
  if (String(e.message).includes("absolute filename")) file = path.resolve(file);
  else throw e;
}

Prevention

When it happens

Trigger: Calling a capture/recording API (e.g. `takeShot`/screenshot with `file`) with a relative path like "shot.png", an empty string, a non-string value, or a path containing "\0" (backends/linux.mjs:55).

Common situations: Passing a filename from CLI args without resolving it; assuming the process cwd applies; concatenating user input into a path.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/linux.mjs:55

function appName(ref) {
  if (ref === undefined) return "";
  if (!ref || typeof ref !== "object" || Array.isArray(ref) || Object.keys(ref).length !== 1 || typeof ref.name !== "string" || !ref.name.trim()) {
    throw Object.assign(new ExecError("Linux accessibility targeting supports only a nonblank app_ref.name; PID and bundle selectors are unavailable"), { code: "unsupported_selector" });
  }
  return ref.name;
}

function rejectWindowSelectors(args) {
  if (Object.hasOwn(args, "app_ref") || Object.hasOwn(args, "window_id")) throw Object.assign(new ExecError("Linux window listing and screenshots do not support app_ref or window_id selectors"), { code: "unsupported_selector" });
}

function assertAppRootWindow(index, windowId) {
  if (windowId !== undefined || (index !== undefined && index !== 0)) throw Object.assign(new ExecError("Linux accessibility paths start at the app root; window selectors are unavailable"), { code: "unsupported_selector" });
}

function outputPath(file) {
  if (typeof file !== "string" || !path.isAbsolute(file) || file.includes("\0")) throw new ExecError("output path must be an absolute filename");
  return file;
}

export function create({ exec } = {}) {
  const run = exec?.run ?? nativeRun;
  const have = exec?.have ?? nativeHave;
  const browser = createBrowser({ platform: "linux" });
  function requireInputOwner() {
    if (exec?.persistentInputOwner !== true) throw Object.assign(new ExecError(
      "This held-input gesture requires a connected Codewhale Computer Use desktop helper so a disconnected client cannot leave keys or buttons pressed. Start the helper and reconnect before retrying."
    ), { code: "input_owner_required" });
  }

  const tools = {};
  let session = null; // "x11" | "wayland"
  let probed = false;
  let lastRaster = null;
  let mouseHeld = false;

View on GitHub (pinned to 73e0f67d83)