micro/go-micro · error
failed to read request: %w
Error message
failed to read request: %w
What it means
The stdio transport reads newline-delimited JSON-RPC requests from stdin. ReadBytes returns nil for a clean EOF (client closed stdin) but any other read error — broken pipe on stdin, I/O error from the attached process — is wrapped as "failed to read request: %w" and returned from Serve, terminating the transport loop.
Source
Thrown at gateway/mcp/stdio.go:95
// Serve starts the stdio transport and processes JSON-RPC requests
func (t *StdioTransport) Serve() error {
t.server.opts.Logger.Printf("[mcp] MCP server started (stdio transport)")
// Read and process requests from stdin
for {
select {
case <-t.ctx.Done():
return nil
default:
}
// Read one line (JSON-RPC request)
line, err := t.reader.ReadBytes('\n')
if err != nil {
if err == io.EOF {
return nil
}
return fmt.Errorf("failed to read request: %w", err)
}
// Parse JSON-RPC request
var req JSONRPCRequest
if err := json.Unmarshal(line, &req); err != nil {
t.sendError(nil, ParseError, "Parse error", err.Error())
continue
}
// Validate JSON-RPC version
if req.JSONRPC != "2.0" {
t.sendError(req.ID, InvalidRequest, "Invalid request", "jsonrpc must be '2.0'")
continue
}
// Handle request
go t.handleRequest(&req)
}View on GitHub (pinned to 24529f1404)
Solutions
- Inspect the wrapped %w error to identify the OS-level cause (e.g. 'file already closed', EPIPE).
- Ensure the parent process keeps the stdin pipe open for the lifetime of the MCP server and closes it cleanly to signal shutdown (clean EOF returns nil).
- Check that your process supervisor isn't closing or re-binding stdin while the server runs.
- Return/handle the error in the caller of Serve and restart the transport if the failure is transient.
Example fix
// before
if err := transport.Serve(); err != nil {
log.Printf("mcp: %v", err) // failed to read request: file already closed
}
// after
if err := transport.Serve(); err != nil {
if errors.Is(err, os.ErrClosed) {
log.Printf("mcp: stdin closed unexpectedly, restarting")
go func() { _ = transport.Serve() }()
} else {
log.Printf("mcp: fatal: %v", err)
}
} Defensive patterns
Strategy: try-catch
Try / catch
if err := transport.Serve(); err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, os.ErrClosed) || errors.Is(err, syscall.EPIPE) {
// stdin stream broken: log, reconnect from parent, or restart transport
} else {
return err
}
} Prevention
- Keep the parent process's stdin pipe open for the server's lifetime; close it cleanly for shutdown (EOF returns nil).
- Test stdio transports under real parent processes (editors/agents), not just with inherited terminal stdin.
- In containers, verify the entrypoint doesn't detach or close stdin.
- Supervise Serve and restart on transient I/O errors.
When it happens
Trigger: StdioTransport.Serve() encounters a non-EOF read error on the stdin reader: the parent process's stdin pipe breaks, the fd is closed unexpectedly, or an OS-level I/O error occurs while blocking on ReadBytes('\n').
Common situations: Parent process (e.g. an MCP client like an editor/agent) crashes or closes the pipe abruptly; running the binary where stdin is a closed/invalid fd; container runtime killing the input stream; malformed pipe handling in a supervisor that restarts the child mid-read.
Related errors
- x402 response already written
- failed to read response: %w
- a2a tasks/resubscribe: %s (%d)
- a2a %s: %s (%d)
- registry is required
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/378ada3dd1d8c23d.
Report an issue: GitHub.