paperclipai/paperclip · error · Error
Invalid sandbox environment variable key: ${key}
Error message
Invalid sandbox environment variable key: ${key} What it means
Thrown by buildLoginShellScript in the E2B provider when any key of the input.env map fails the isValidShellEnvKey regex ^[A-Za-z_][A-Za-z0-9_]*$. The keys are emitted verbatim into an `exec env KEY=...` line of a login-shell wrapper, so an invalid shell identifier would either break the wrapper or inject shell syntax; the plugin rejects it up front.
Source
Thrown at packages/plugins/sandbox-providers/e2b/src/plugin.ts:170
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
}
// Source the user's login profiles before exec so commands run with the same
// PATH the user sees in an interactive shell. e2b's `sandbox.commands.run`
// otherwise spawns a non-login, non-interactive shell whose PATH does not
// include npm-globals or anything else the template installs via
// .profile/.bashrc — which makes the hello probe fail with
// `exec: <cli>: not found` even when the binary is on disk. The wrapper no
// longer sources `nvm.sh`; the sandbox image supplies `node` on the PATH.
function buildLoginShellScript(input: {
command: string;
args: string[];
env?: Record<string, string>;
}): string {
const env = input.env ?? {};
for (const key of Object.keys(env)) {
if (!isValidShellEnvKey(key)) {
throw new Error(`Invalid sandbox environment variable key: ${key}`);
}
}
const envArgs = Object.entries(env)
.filter((entry): entry is [string, string] => typeof entry[1] === "string")
.map(([key, value]) => `${key}=${shellQuote(value)}`);
const commandParts = [shellQuote(input.command), ...input.args.map(shellQuote)].join(" ");
const execLine = envArgs.length > 0
? `exec env ${envArgs.join(" ")} ${commandParts}`
: `exec ${commandParts}`;
return [
'if [ -f /etc/profile ]; then . /etc/profile >/dev/null 2>&1 || true; fi',
'if [ -f "$HOME/.profile" ]; then . "$HOME/.profile" >/dev/null 2>&1 || true; fi',
// .bash_profile typically sources .bashrc itself; only source .bashrc
// directly when no .bash_profile exists to avoid re-running idempotency-
// sensitive setup (nvm, PATH prepends) twice on templates that wire
// .bash_profile -> .bashrc.
'if [ -f "$HOME/.bash_profile" ]; then . "$HOME/.bash_profile" >/dev/null 2>&1 || true; elif [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" >/dev/null 2>&1 || true; fi',
'if [ -f "$HOME/.zprofile" ]; then . "$HOME/.zprofile" >/dev/null 2>&1 || true; fi',View on GitHub (pinned to 67001ec6eb)
Solutions
- Sanitize env keys before passing them to the sandbox: replace non-[A-Za-z0-9_] chars with '_', uppercase, and strip leading digits.
- Drop env vars whose keys cannot be converted to valid shell identifiers instead of forwarding them.
- Validate the env map at config-ingest time so the bad key is reported at save, not at exec.
Example fix
// before
const env = { 'MY-VAR': 'x', '1ST': 'y' };
// after
function sanitizeEnvKey(k) {
const cleaned = k.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
return cleaned.replace(/^[0-9]+/, '');
}
const env = Object.fromEntries(
Object.entries(raw).map(([k, v]) => [sanitizeEnvKey(k), v]).filter(([k]) => k)
); Defensive patterns
Strategy: validation
Validate before calling
const SHELL_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
function sanitizeEnvForShell(env: Record<string, string>): Record<string, string> {
const out: Record<string, string> = {};
for (const [k, v] of Object.entries(env)) {
const key = k.toUpperCase().replace(/[^A-Z0-9_]/g, '_').replace(/^[0-9]+/, '');
if (key && SHELL_KEY.test(key)) out[key] = v;
}
return out;
} Type guard
function isValidShellEnvKey(value: string): boolean {
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
} Try / catch
try {
buildLoginShellScript({ command, args, env });
} catch (err) {
if (err instanceof Error && /Invalid sandbox environment variable key/.test(err.message)) {
// strip/sanitize offending keys and retry
return buildLoginShellScript({ command, args, env: sanitizeEnvForShell(env) });
}
throw err;
} Prevention
- Validate env keys at config-ingest time, not at exec time.
- Reject or sanitize keys containing '-', '.', leading digits, or spaces.
When it happens
Trigger: Passing an env map to a sandbox exec/probe whose keys contain characters illegal in a POSIX shell variable name: dashes (MY-VAR), dots (my.var), leading digits (1ST), spaces, or non-ASCII.
Common situations: Driver config copied from a YAML/JSON that allowed arbitrary key names; user-supplied env vars forwarded without sanitization; keys like 'NODE_ENV' pass but 'npm-config-foo' or 'GIT.Committer' do not.
Related errors
- Profile name is required
- Invalid --persona value. Use board or agent.
- Unsupported export format: ${value}
- Invalid integer value: ${value}
- --version is only supported for npm package installs, not lo
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/93af642618eecb99.
Report an issue: GitHub.