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

  1. 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).
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of vercel-labs/agent-browser@548b159b30 (2026-08-16). Data as JSON: /api/errors/0e6ecc9d46c0f1d8. Report an issue: GitHub.