vercel/ai · error · TypeError

Stdio MCP commands and arguments must not contain line break

Error message

Stdio MCP commands and arguments must not contain line breaks on Windows.

What it means

On Windows, spawn treats newlines in a command or argument as argument separators, enabling command/argument injection into stdio MCP servers. createChildProcess proactively throws a TypeError if the command or any arg contains \r or \n when process.platform is 'win32'.

Source

Thrown at packages/mcp/src/tool/mcp-stdio/create-child-process.ts:14

import type { ChildProcess } from 'node:child_process';
import spawn from 'cross-spawn';
import { getEnvironment } from './get-environment';
import type { StdioConfig } from './mcp-stdio-transport';

export function createChildProcess(
  config: StdioConfig,
  signal: AbortSignal,
): ChildProcess {
  if (
    globalThis.process.platform === 'win32' &&
    [config.command, ...(config.args ?? [])].some(value => /[\r\n]/.test(value))
  ) {
    throw new TypeError(
      'Stdio MCP commands and arguments must not contain line breaks on Windows.',
    );
  }

  return spawn(config.command, config.args ?? [], {
    env: getEnvironment(config.env),
    stdio: ['pipe', 'pipe', config.stderr ?? 'inherit'],
    shell: false,
    signal,
    windowsHide: globalThis.process.platform === 'win32' && isElectron(),
    cwd: config.cwd,
  });
}

function isElectron() {
  return 'type' in globalThis.process;
}

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Remove line breaks: split multi-line commands into separate args entries or trim/replace [\r\n] before constructing the transport
  2. Validate command/args with a regex like /[\r\n]/ before creating StdioMCPTransport, especially for dynamic or user-provided config
  3. Run the same config through validation on all platforms, or normalize config per-OS before spawn

Example fix

// before
new StdioMCPTransport({ command: `npx\n-y mcp-server` });
// after
new StdioMCPTransport({ command: 'npx', args: ['-y', 'mcp-server'] });
Defensive patterns

Strategy: validation

Validate before calling

function assertNoLineBreaks(config) {
  if (process.platform === 'win32' &&
      [config.command, ...(config.args ?? [])].some(v => /[\r\n]/.test(v))) {
    throw new TypeError('Stdio MCP command/args must not contain line breaks on Windows');
  }
}
assertNoLineBreaks(config);

Type guard

function isSafeStdioConfig(config: { command: string; args?: string[] }): boolean {
  return ![config.command, ...(config.args ?? [])].some(v => /[\r\n]/.test(v));
}

Try / catch

try {
  const client = await createMCPClient({ transport: new StdioMCPTransport(config) });
} catch (error) {
  if (error instanceof TypeError && error.message.includes('line breaks')) {
    // sanitize config.command/args and retry
  } else throw error;
}

Prevention

When it happens

Trigger: Creating a StdioMCPTransport whose config.command or config.args contains a line break (from shell-interpolated strings, copied multi-line commands, or user/LLM-supplied config) while running on Windows.

Common situations: Building the command from template strings that include newlines; passing user-controlled config into stdio transport on Windows; splitting commands incorrectly instead of using separate args entries.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/12f1238aa837f168. Report an issue: GitHub.