mastra-ai/mastra · error
Invalid environment variable name: "${key}"
Error message
Invalid environment variable name: "${key}" What it means
buildLaunchScript generates a POSIX shell script that exports each env var before starting the server. Keys are validated against /^[A-Za-z_][A-Za-z0-9_]*$/ because they're interpolated directly into `export KEY=...`; an invalid key is rejected to prevent broken or malicious shell output.
Source
Thrown at deployers/sandbox/src/engine.ts:196
*/
export function buildLaunchScript(opts: { remoteDir: string; port: number; env: Record<string, string> }): string {
const lines = ['#!/bin/sh', `cd ${shellQuote(opts.remoteDir)}`];
// MASTRA_AUTO_DETECT_URL so Studio connects to the sandbox's public URL
// (same origin) instead of localhost:4111 — overridable. PORT and
// MASTRA_HOST are applied AFTER custom env: networking (`getPortUrl`) and
// health checks target the configured port, and the server must bind
// 0.0.0.0 to be reachable through the public port proxy. Change the port
// via the deploy `port` option, not env.
const env: Record<string, string> = {
MASTRA_AUTO_DETECT_URL: 'true',
...opts.env,
PORT: String(opts.port),
MASTRA_HOST: '0.0.0.0',
};
for (const [key, value] of Object.entries(env)) {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
throw new Error(`Invalid environment variable name: "${key}"`);
}
lines.push(`export ${key}=${shellQuote(value)}`);
}
lines.push(`echo $$ > ${shellQuote(SERVER_PIDFILE)}`);
lines.push(`exec node index.mjs >> ${shellQuote(SERVER_LOGFILE)} 2>&1`);
return lines.join('\n') + '\n';
}
/** Create a gzipped tarball of the directory contents (excluding node_modules). */
export async function createTarball(dir: string): Promise<Buffer> {
const tmp = await mkdtemp(join(tmpdir(), 'mastra-sandbox-'));
const tarPath = join(tmp, 'deploy.tgz');
try {
await execFileAsync('tar', ['-czf', tarPath, '--exclude=node_modules', '-C', dir, '.']);
return await readFile(tarPath);
} finally {
await rm(tmp, { recursive: true, force: true });View on GitHub (pinned to 75dd419e61)
Solutions
- Rename the offending env key to a valid identifier (letters, digits, underscores; not starting with a digit)
- Sanitize env maps before passing: strip/convert illegal characters (dots/dashes → underscores)
- Validate the keys in your config loading step before calling the deploy
- Check the error message for which exact key was rejected
Example fix
// before
await deployToSandbox(sandbox, { env: { 'MY-VAR': 'x' } });
// after
await deployToSandbox(sandbox, { env: { MY_VAR: 'x' } }); Defensive patterns
Strategy: validation
Validate before calling
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
for (const key of Object.keys(env)) {
if (!ENV_KEY_RE.test(key)) throw new Error(`invalid env key: ${key}`);
} Prevention
- Normalize keys (replace - and . with _) when loading env maps from config files
- Reject invalid keys at config-load time, not deploy time
- Never derive env keys from untrusted user input
When it happens
Trigger: Passing env keys that are not valid shell identifiers — e.g. containing dashes, dots, spaces, digits at the start, or empty keys — via the env option to buildLaunchScript (used by deployToSandbox's launchScript).
Common situations: Parsing env files with odd keys into the env option; passing metadata-like keys (e.g. 'my-var' or 'app.name') instead of valid env names; programmatic env maps built from untrusted input.
Related errors
- Unknown worker resource limit: ${name}.
- Worker resourceLimits.${name} must be a positive safe intege
- Resource-limit preflight command failed.
- Worker input exceeds inputLimitBytes (${data.byteLength} > $
- Unable to read worker ${stream}.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/b1506d4a23ef3c76.
Report an issue: GitHub.