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
- Remove line breaks: split multi-line commands into separate args entries or trim/replace [\r\n] before constructing the transport
- Validate command/args with a regex like /[\r\n]/ before creating StdioMCPTransport, especially for dynamic or user-provided config
- 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
- Sanitize or split any dynamic/user-provided command strings before building the transport
- Always pass multi-token commands as separate args entries, never as one newline-containing string
- Run this validation on every platform to catch config issues early
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
- Tool approval signature verification failed for approval "${
- Continuation signing key must not be empty.
- Invalid host tool relay credential.
- ACP runtime environment key ${JSON.stringify(key)} cannot be
- Invalid Cline history file name: ${historyFileName}
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/12f1238aa837f168.
Report an issue: GitHub.