Hmbown/CodeWhale · error · ExecError

open_application needs a plain executable/desktop name

Error message

open_application needs a plain executable/desktop name

What it means

The linux backend's open_application only launches plain executable or desktop-entry names (e.g. 'firefox', 'org.gnome.Nautilus'). The library throws this when the resolved target (name or bundle_id) is missing or contains characters outside letters, digits, space, dot, underscore, dash — a guard against shell/special-character injection and misuse of macOS-style bundle IDs or URLs.

Solutions

  1. Pass a plain executable or desktop-entry name, e.g. open_application({ name: 'firefox' }).
  2. Strip path prefixes and shell metacharacters from the target before calling.
  3. To open a URL, launch the browser app by name with the URL handled separately instead of relying on urlArg.
  4. If the app only has a desktop entry, use its desktop file name (e.g. 'org.gnome.Nautilus').

Example fix

// before
await backend.open_application({ name: '/usr/bin/google-chrome --incognito' });
// after
await backend.open_application({ name: 'google-chrome' });
Defensive patterns

Strategy: validation

Validate before calling

const target = opts.name ?? opts.bundle_id;
if (!/^[A-Za-z0-9][A-Za-z0-9 ._-]*$/.test(target ?? '')) throw new Error('open_application needs a plain executable/desktop name');

Type guard

const isValidAppTarget = (t) => typeof t === 'string' && /^[A-Za-z0-9][A-Za-z0-9 ._-]*$/.test(t);

Prevention

When it happens

Trigger: Calling open_application with no name/bundle_id; passing a bundle_id that is only used on macOS (e.g. 'com.apple.Safari'); passing a URL to urlArg while name and bundle_id are absent; names containing '/', ':', '@', quotes, or starting with a non-alphanumeric character.

Common situations: Porting cross-platform automation code that reuses macOS bundle identifiers; trying to open a website by URL on Linux where that parameter is ignored; building the name from user input containing paths like '/usr/bin/firefox' or shell metacharacters.

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/90f8d3e463143f22. Report an issue: GitHub.

Appendix: source

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

        const windows = [];
        const walk = (n) => {
          if (n.type === "con" && n.name) windows.push({ id: String(n.id), title: n.name, wm_class: n.app_id ?? null, position: { x: n.rect?.x, y: n.rect?.y }, size: { w: n.rect?.width, h: n.rect?.height }, focused: !!n.focused });
          (n.nodes ?? []).forEach(walk);
          (n.floating_nodes ?? []).forEach(walk);
        };
        walk(tryJson(r.stdout, {}));
        return { windows };
      }
      if (session === "wayland" && tools.hyprctl) {
        const r = await run("hyprctl", ["-j", "clients"], { timeoutMs: 15_000 });
        const clients = tryJson(r.stdout, []);
        return { windows: clients.map((c) => ({ id: String(c.address), title: c.title, wm_class: c.class, position: { x: c.at?.[0], y: c.at?.[1] }, size: { w: c.size?.[0], h: c.size?.[1] }, focused: !!c.focused })) };
      }
      throw new ExecError("list_windows needs wmctrl (X11), swaymsg (sway) or hyprctl (hyprland)");
    },
    open_application: async ({ name, bundle_id: bid, url: urlArg, activate } = {}) => {
      const target = name ?? bid;
      if (!target || !/^[A-Za-z0-9][A-Za-z0-9 ._-]*$/.test(target)) throw new ExecError("open_application needs a plain executable/desktop name");
      // activate defaults to background: on X11 a new window grabs focus, so
      // remember the active window and hand focus back after the launch.
      let prevWindow = null;
      if (activate !== true) {
        try {
          await probeSession();
          if (session === "x11" && tools.xdotool) {
            const active = await run("xdotool", ["getactivewindow"], { timeoutMs: 3_000 });
            if (active.code === 0 && /^\d+$/.test(active.stdout.trim())) prevWindow = active.stdout.trim();
          }
        } catch { /* no session/tools — the launch itself is still fine */ }
      }
      spawnDetached(target, urlArg ? [urlArg] : [], "", true);
      await new Promise((r) => setTimeout(r, 500));
      let focusRestored = false;
      if (prevWindow) {
        try {
          focusRestored = (await run("xdotool", ["windowactivate", prevWindow], { timeoutMs: 3_000 })).code === 0;

View on GitHub (pinned to 73e0f67d83)