Hmbown/CodeWhale · error · ExecError
bad_args
bad_args
Error message
browser navigate/start need a url (http:// or https:// or about:blank)
What it means
checkBrowserUrl validates the url argument for browser navigate/start. An empty string, whitespace-only value, or non-string (undefined/null) fails the first guard and throws badArgs demanding a URL. The plugin only allows http://, https://, and about:blank targets.
Solutions
- Provide a url argument with a valid http:// or https:// URL, or the literal about:blank.
- If the target is a local file, serve it over http (e.g. a local dev server) instead of file://.
- Trim user/config input before passing; an all-whitespace value is treated as missing.
- Validate the field exists before dispatching the browser action.
Example fix
// before
await browser({ action: "navigate", url: "" });
// after
await browser({ action: "navigate", url: "https://example.com" }); Defensive patterns
Strategy: validation
Validate before calling
if (typeof url !== "string" || !url.trim()) throw new Error("navigate/start require a non-empty url"); Type guard
const hasUrl = (a) => typeof a?.url === "string" && a.url.trim().length > 0;
Try / catch
try { await browser(action) } catch (e) { if (String(e.message).includes("need a url")) { throw new Error(`caller bug: missing url for ${action.action}`); } throw e; } Prevention
- Validate the url field before dispatching browser actions
- Trim config/user input before assigning url
- Default to about:blank when no target is intended
When it happens
Trigger: Calling browser navigate or browser start without a url argument, with url: "", or with a whitespace-only url.
Common situations: An LLM omitting the url field; a form/config value that is empty; constructing the action object dynamically and leaving url undefined; forgetting that file:// and chrome:// are also rejected here (different message) but empty input hits this one.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- browser URL cannot be empty
- Checkpoint continuation requires a source agent
- Model ' ' requires a voice design prompt. Pass…
- open_application needs name, bundle_id or pid
- 1
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/bb4dce92bc70cf29.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/browser-cdp.mjs:36
import path from "node:path";
import crypto from "node:crypto";
import { spawn } from "node:child_process";
import { ExecError, currentSignal } from "./exec.mjs";
import { stateDir } from "./registry.mjs";
const APPLICATIONS = ["Google Chrome", "Chromium", "Brave Browser", "Microsoft Edge"];
const LINUX_BINARIES = ["google-chrome", "chromium", "chromium-browser", "brave-browser", "microsoft-edge"];
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const badArgs = (message) => Object.assign(new ExecError(message), { code: "bad_args" });
function defaultRecordingsDir() {
return process.env.CODEWHALE_CU_RECORDINGS_DIR || path.join(stateDir(), "recordings");
}
/** Only http(s) and about:blank can be navigated to; everything else is refused. */
export function checkBrowserUrl(url) {
if (typeof url !== "string" || !url.trim()) throw badArgs("browser navigate/start need a url (http:// or https:// or about:blank)");
const trimmed = url.trim();
if (/^about:blank$/i.test(trimmed)) return trimmed;
let parsed;
try { parsed = new URL(trimmed); } catch { throw badArgs(`"${trimmed}" is not a URL`); }
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw badArgs(`only http(s):// and about:blank URLs can be opened (got "${parsed.protocol}//")`);
}
return parsed.href;
}
/** Locate a Chromium-family browser app or binary; CODEWHALE_CU_BROWSER_APP overrides. */
export function findBrowserApp(platform = process.platform, env = process.env, exists = fs.existsSync) {
if (env.CODEWHALE_CU_BROWSER_APP) return env.CODEWHALE_CU_BROWSER_APP;
if (platform === "darwin") {
for (const name of APPLICATIONS) {
for (const root of ["/Applications", path.join(os.homedir(), "Applications")]) {
const candidate = path.posix.join(root, `${name}.app`);
if (exists(candidate)) return candidate;View on GitHub (pinned to 73e0f67d83)