mastra-ai/mastra · error · Error

Refusing to pass unsafe argument to shell command: ${JSON.st

Error message

Refusing to pass unsafe argument to shell command: ${JSON.stringify(arg)}

What it means

createChildProcessLogger spawns child processes with shell:true, which makes each argument a shell-injection risk. Before spawning it validates every arg against SAFE_SHELL_ARG and throws if an argument contains characters outside the safe set. The error names the rejected argument via JSON.stringify so it is visible even if it contains quotes or control characters.

Source

Thrown at packages/deployer/src/deploy/log.ts:36

    },
  });
};

/**
 * Args are joined into a shell command (`shell: true` is required for package
 * manager shims on Windows), so only allow characters that appear in package
 * specifiers and CLI flags — never shell metacharacters (CodeQL
 * js/shell-command-constructed-from-input).
 */
const SAFE_SHELL_ARG = /^[\w@%+=:,./^~-]*$/;

export function createChildProcessLogger({ logger, root }: { logger: IMastraLogger; root: string }) {
  const pinoStream = createPinoStream(logger);
  return async ({ cmd, args, env }: { cmd: string; args: string[]; env: Record<string, string> }) => {
    try {
      for (const arg of args) {
        if (!SAFE_SHELL_ARG.test(arg)) {
          throw new Error(`Refusing to pass unsafe argument to shell command: ${JSON.stringify(arg)}`);
        }
      }
      const subprocess = spawn(cmd, args, {
        cwd: root,
        shell: true,
        env,
        // No stdin for the child process — it doesn't need interactive input
        stdio: ['ignore', 'pipe', 'pipe'],
      });

      let stdout = '';
      let stderr = '';
      subprocess.stdout?.on('data', chunk => {
        stdout += chunk.toString();
      });
      subprocess.stderr?.on('data', chunk => {
        stderr += chunk.toString();
      });

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Remove/escape unsafe characters from the offending argument (rename directories to avoid spaces or shell metacharacters).
  2. Ensure arguments are passed as separate array items, not as one pre-joined command string.
  3. Sanitize or validate dynamic values (paths, names) before invoking deployer commands that spawn child processes.

Example fix

// before
spawn('pnpm', ['install --prefer-offline'], { shell: true })
// after
spawn('pnpm', ['install', '--prefer-offline'], { shell: true })
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_SHELL_ARG = /^[A-Za-z0-9_@%+=:,./-]+$/;
args.forEach(arg => { if (!SAFE_SHELL_ARG.test(arg)) throw new Error(`Unsafe shell arg: ${JSON.stringify(arg)}`); });

Try / catch

try {
  await runCommand({ cmd, args, env });
} catch (e) {
  if (String(e.message).startsWith('Refusing to pass unsafe argument')) {
    console.error('Sanitize args (no spaces/quotes/metacharacters) before retrying.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Running a command (install/build/start) where any element of args contains characters disallowed by SAFE_SHELL_ARG — typically spaces, quotes, semicolons, backticks, $, or newline characters — passed through createChildProcessLogger.

Common situations: File paths with spaces on Windows/macOS; project names or env-derived values with special characters; a package name or directory interpolated into an arg from user input or CI variables.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/f1677f09750c43d6. Report an issue: GitHub.