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
- Pass a plain executable or desktop-entry name, e.g. open_application({ name: 'firefox' }).
- Strip path prefixes and shell metacharacters from the target before calling.
- To open a URL, launch the browser app by name with the URL handled separately instead of relying on urlArg.
- 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
- Validate app names against the same regex the backend uses before calling
- Never interpolate paths or flags into the name field
- Keep a per-platform launch map: bundle_id for macOS, desktop names for Linux
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
- click supports left with 1-3 clicks, right x1 or middle x1
- Invalid native whale body.
- Invalid pet PCM range.
- Invalid pet voice.
- no screenshot taken yet on this computer — call screenshot…
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)