paperclipai/paperclip · error
Invalid sandbox environment variable key
Error message
Invalid sandbox environment variable key: ${key} What it means
Env-key sanitization in buildLoginShellScript for the Daytona driver: an environment variable key failed isValidShellEnvKey, which rejects keys that are not valid identifiers for the generated login-shell export line. This blocks shell metacharacter injection through env var names before the command string is assembled.
Solutions
- Use a valid environment variable key (letters, digits, underscore, not starting with a digit).
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at packages/plugins/sandbox-providers/daytona/src/plugin.ts:826 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-18).
Data as JSON: /api/errors/65818980626ad75f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/daytona/src/plugin.ts:873
if (arg === "submodule") {
const next = args.slice(i + 1).find(a => !a.startsWith("-"));
return next === "update";
}
return false;
}
return false;
}
// Build the one-shot exec command. Daytona's `executeCommand` runs the script
// in a non-login shell, so it does not source `/etc/profile` on its own. The
// Daytona reference image puts `node`, `claude`, and the other CLIs on the PATH
// through `/etc/profile.d/00-restore-env.sh`, which only `/etc/profile` sources.
// So the wrapper sources the login profiles itself; a non-login shell is then
// enough to resolve the CLIs. The wrapper no longer sources `nvm.sh`; the
// sandbox image supplies `node` on the PATH. See the sandbox runtime
// requirements document.
function buildLoginShellScript(input: {
command: string;
args: string[];
cwd?: string;
env?: Record<string, string>;
stdinPath?: string;
}): string {
const callerEnv = input.env ?? {};
for (const key of Object.keys(callerEnv)) {
if (!isValidShellEnvKey(key)) {
throw new Error(`Invalid sandbox environment variable key: ${key}`);
}
}
// Caller env takes priority over noninteractive git credential defaults
const env = { ...NONINTERACTIVE_GIT_ENV, ...callerEnv };
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 redirectedCommand = input.stdinPathView on GitHub (pinned to 3f1d897a7c)