neoclide/coc.nvim · error · Error
Process created without stdio streams
Error message
Process created without stdio streams
What it means
When spawning the language server as a child process with stdio transport, the client asserts stdin/stdout/stderr are all non-null. If Node returned null streams (process failed to get pipes), the process is SIGKILLed and this error is thrown instead of hanging on a dead transport.
Source
Thrown at src/language-client/index.ts:266
protected createMessageTransports(encoding: string): Promise<MessageTransports> {
function getEnvironment(env: any, fork: boolean): any {
if (!env && !fork) {
return undefined
}
let result: any = Object.create(null)
Object.keys(process.env).forEach(key => result[key] = process.env[key])
if (env) {
Object.keys(env).forEach(key => result[key] = env[key])
}
return result
}
function assertStdio(process: ChildProcess): asserts process is ChildProcessWithoutNullStreams {
if (process.stdin === null || process.stdout === null || process.stderr === null) {
process.kill('SIGKILL')
throw new Error('Process created without stdio streams')
}
}
function logMessage(kind: string, data: string, outputChannel: OutputChannel): void {
let msg = `[${kind} - ${currentTimeStamp()}] ${data}`
outputChannel.appendLine(msg)
}
function pipeStdoutToLogOutputChannel(input: stream.Readable, outputChannel: OutputChannel) {
readline.createInterface({
input,
crlfDelay: Infinity,
terminal: false,
historySize: 0,
}).on('line', data => logMessage('Stdout', data, outputChannel))
}
function pipeStderrToLogOutputChannel(input: stream.Readable, outputChannel: OutputChannel) {View on GitHub (pinned to 50e974d969)
Solutions
- Do not override stdio in child_process spawn options for the server
- Verify the server command is a real executable that keeps stdio attached
- Remove shell wrappers (cmd /c, .bat indirection) or ensure they forward stdio
- Check Node version; spawn with the correct ChildProcessSpawnOptions
Example fix
// before
const serverOptions: ServerOptions = () => spawn(cmd, args, { stdio: 'ignore' })
// after
const serverOptions: ServerOptions = () => spawn(cmd, args) // stdio pipes created by default Defensive patterns
Strategy: validation
Validate before calling
// Before starting the server, verify the command exists and options don't override stdio
if (!opts.stdio && !commandExists(serverCommand)) {
throw new Error(`Server command not found: ${serverCommand}`)
} Type guard
function hasNullStreams(p: ChildProcess): boolean {
return p.stdin === null || p.stdout === null || p.stderr === null
} Try / catch
try {
const client = new LanguageClient(id, name, serverOptions, clientOptions)
await client.start()
} catch (e) {
if (String(e.message).includes('without stdio streams')) {
console.error('Server spawn failed to attach stdio; check command/options')
} else { throw e }
} Prevention
- Never set stdio overrides in spawn options for the language server
- Verify the server binary runs standalone and keeps stdio open
- Avoid shell wrappers that swallow stdio
- Check process.exit/error events on the child for early termination
When it happens
Trigger: server module launched with a command that Node could not attach stdio pipes to (e.g. executable options overriding stdio, spawn environment issues, or a command that immediately detaches).
Common situations: Server options inadvertently setting stdio: 'ignore'; spawning GUI wrappers that don't inherit stdio; resource limits preventing pipe creation; wrong executable path combined with shell wrappers.
Related errors
- Required root pattern not resolved.
- Stopping the server timed out
- Transport kind ipc is not supported for command executable
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/a77ec8772eb47618.
Report an issue: GitHub.