vercel-labs/agent-browser · error
Invalid environment variable name: ${key}
Error message
Invalid environment variable name: ${key} What it means
Thrown by formatShellEnv (shared.ts:133) while buildShellCommand renders the env prefix `KEY=value ...` that is prepended to the agent-browser shell command. Every env key must match SAFE_ENV_KEY (/^[A-Za-z_][A-Za-z0-9_]*$/ in packages/@agent-browser/sandbox/src/shared.ts:41) because the key is interpolated unquoted into a shell command line. Any character outside letters, digits, and underscores, or a key that starts with a digit, is rejected to prevent shell injection. Keys whose value is undefined are silently filtered out before this check runs.
Source
Thrown at packages/@agent-browser/sandbox/src/shared.ts:133
if (result.exitCode !== 0) {
throw new AgentBrowserCommandError(result);
}
return result;
}
export function defaultSessionName(prefix: string, id: string): string {
const safePrefix = sanitizeSessionPart(prefix) || "agent-browser";
const safeId = sanitizeSessionPart(id) || "default";
return truncateSessionName(`${safePrefix}-${safeId}`);
}
function formatShellEnv(env: Readonly<Record<string, string | undefined>> | undefined): string {
if (env === undefined) return "";
return Object.entries(env)
.filter((entry): entry is [string, string] => entry[1] !== undefined)
.map(([key, value]) => {
if (!SAFE_ENV_KEY.test(key)) {
throw new Error(`Invalid environment variable name: ${key}`);
}
return `${key}=${quoteShellArg(value)}`;
})
.join(" ");
}
function parseJson<TJson>(value: string): TJson | null {
try {
return JSON.parse(value) as TJson;
} catch {
return null;
}
}
function sanitizeSessionPart(value: string): string {
return value.trim().replaceAll(/[^A-Za-z0-9_-]+/g, "-").replaceAll(/^-+|-+$/g, "");
}
View on GitHub (pinned to 548b159b30)
Solutions
- Rename the offending key so it starts with a letter or underscore and contains only [A-Za-z0-9_] (the error message prints the exact key that failed).
- If the env map comes from untrusted input, sanitize keys before calling buildShellCommand: strip invalid characters or map them to underscores, and drop keys that end up empty.
- Keep an allowlist of the env names you actually intend to pass and construct the env object only from that list instead of forwarding a whole dictionary.
- If the value (not the name) is the problem you want to ship, note that values are handled safely by quoteShellArg; only the NAME is validated, so fix the name rather than quoting it.
Example fix
// before
const command = buildShellCommand(["open", url], {
env: { "MY-VAR": "value", API_TOKEN: token },
});
// after
const command = buildShellCommand(["open", url], {
env: { MY_VAR: "value", API_TOKEN: token },
}); Defensive patterns
Strategy: validation
Validate before calling
const SAFE_ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
function buildSafeEnv(env: Readonly<Record<string, string | undefined>>): Record<string, string> {
const safe: Record<string, string> = {};
for (const [key, value] of Object.entries(env)) {
if (value === undefined) continue;
if (!SAFE_ENV_KEY.test(key)) {
throw new Error(`Invalid environment variable name: ${key}`);
}
safe[key] = value;
}
return safe;
}
// run BEFORE buildShellCommand
const env = buildSafeEnv({ "MY-VAR": "x", OK_VAR: "y" }); // fails fast on MY-VAR Type guard
function isValidEnvKey(key: string): boolean {
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key);
}
function assertValidEnv(env: Readonly<Record<string, string | undefined>>): void {
for (const key of Object.keys(env)) {
if (!isValidEnvKey(key)) {
throw new Error(`Invalid environment variable name: ${key}`);
}
}
} Try / catch
try {
const command = buildShellCommand(args, { env });
} catch (error) {
if (error instanceof Error && error.message.startsWith("Invalid environment variable name:")) {
const badKey = error.message.split(": ")[1];
// report the offending key to the user/config layer instead of retrying
throw new Error(`Config error: env key '${badKey}' must match /^[A-Za-z_][A-Za-z0-9_]*$/`);
}
throw error;
} Prevention
- Derive env objects from a fixed allowlist of variable names instead of forwarding whole dictionaries from user input or headers.
- When mapping external names to env, normalize them once (replace invalid characters with _, uppercase) and cache the mapping.
- Add a unit test that runs your env construction through /^[A-Za-z_][A-Za-z0-9_]*$/ so regressions fail in CI, not at shell-build time.
- Remember undefined values are silently dropped; filter them yourself if you want typo'd keys to be caught.
When it happens
Trigger: Calling buildShellCommand(args, { env }) (shared.ts:88-94) with an env object whose key contains a hyphen, dot, space, equals sign, or slash (e.g. { "MY-VAR": "1" }, { "NEXT_PUBLIC.foo": "x" }), an empty-string key, or a key starting with a digit ("1KEY"). The throw happens synchronously, before the sandbox command is ever issued.
Common situations: Forwarding arbitrary request headers or cookie names as env keys; copying variable names from YAML/Next.js configs that allow dots or hyphens; building env objects dynamically from user input or from Object.keys(process.env) on platforms exposing unusual names; test fixtures reusing display-case labels as keys.
Related errors
- Invalid system dependency name: ${JSON.stringify(name)}
- The browser tools require an eve sandbox. Configure agent/sa
- agent-browser requires an Eve sandbox. Configure agent/sandb
- @agent-browser/sandbox/vercel requires @vercel/sandbox. Inst
- @vercel/sandbox did not export Sandbox.
AI-assisted analysis of vercel-labs/agent-browser@548b159b30 (2026-08-16).
Data as JSON: /api/errors/0e6ecc9d46c0f1d8.
Report an issue: GitHub.