{"record":{"id":"b1506d4a23ef3c76","repo":"mastra-ai/mastra","slug":"invalid-environment-variable-name-key","errorCode":null,"errorMessage":"Invalid environment variable name: \"${key}\"","messagePattern":"Invalid environment variable name: \"(.+?)\"","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"deployers/sandbox/src/engine.ts","lineNumber":196,"sourceCode":" */\nexport function buildLaunchScript(opts: { remoteDir: string; port: number; env: Record<string, string> }): string {\n  const lines = ['#!/bin/sh', `cd ${shellQuote(opts.remoteDir)}`];\n\n  // MASTRA_AUTO_DETECT_URL so Studio connects to the sandbox's public URL\n  // (same origin) instead of localhost:4111 — overridable. PORT and\n  // MASTRA_HOST are applied AFTER custom env: networking (`getPortUrl`) and\n  // health checks target the configured port, and the server must bind\n  // 0.0.0.0 to be reachable through the public port proxy. Change the port\n  // via the deploy `port` option, not env.\n  const env: Record<string, string> = {\n    MASTRA_AUTO_DETECT_URL: 'true',\n    ...opts.env,\n    PORT: String(opts.port),\n    MASTRA_HOST: '0.0.0.0',\n  };\n  for (const [key, value] of Object.entries(env)) {\n    if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {\n      throw new Error(`Invalid environment variable name: \"${key}\"`);\n    }\n    lines.push(`export ${key}=${shellQuote(value)}`);\n  }\n\n  lines.push(`echo $$ > ${shellQuote(SERVER_PIDFILE)}`);\n  lines.push(`exec node index.mjs >> ${shellQuote(SERVER_LOGFILE)} 2>&1`);\n  return lines.join('\\n') + '\\n';\n}\n\n/** Create a gzipped tarball of the directory contents (excluding node_modules). */\nexport async function createTarball(dir: string): Promise<Buffer> {\n  const tmp = await mkdtemp(join(tmpdir(), 'mastra-sandbox-'));\n  const tarPath = join(tmp, 'deploy.tgz');\n  try {\n    await execFileAsync('tar', ['-czf', tarPath, '--exclude=node_modules', '-C', dir, '.']);\n    return await readFile(tarPath);\n  } finally {\n    await rm(tmp, { recursive: true, force: true });","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/deployers/sandbox/src/engine.ts#L178-L214","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nawait deployToSandbox(sandbox, { env: { 'MY-VAR': 'x' } });\n// after\nawait deployToSandbox(sandbox, { env: { MY_VAR: 'x' } });","handlingStrategy":"validation","validationCode":"const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\nfor (const key of Object.keys(env)) {\n  if (!ENV_KEY_RE.test(key)) throw new Error(`invalid env key: ${key}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["validation","env","shell","sandbox"],"backgroundTag":"invalid-identifier","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}