ipfs/kubo · error
failed to start server, process closing
Error message
failed to start server, process closing
What it means
ServeWithReady checks the node's context before constructing and starting the HTTP server (RPC API or gateway). If the node's context is already cancelled at startup time, kubo aborts with this error instead of launching a server on a dead node. It is a lifecycle guard: the process is closing, so serving traffic would be pointless.
Source
Thrown at core/corehttp/corehttp.go:113
//
// Passing nil for ready is equivalent to calling Serve().
func ServeWithReady(node *core.IpfsNode, lis net.Listener, ready chan<- struct{}, options ...ServeOption) error {
// make sure we close this no matter what.
defer lis.Close()
handler, err := MakeHandler(node, lis, options...)
if err != nil {
return err
}
addr, err := manet.FromNetAddr(lis.Addr())
if err != nil {
return err
}
select {
case <-node.Context().Done():
return fmt.Errorf("failed to start server, process closing")
default:
}
server := &http.Server{
Handler: handler,
}
var serverError error
serverClosed := make(chan struct{})
go func() {
if ready != nil {
close(ready)
}
serverError = server.Serve(lis)
close(serverClosed)
}()
// wait for server to exit.View on GitHub (pinned to 329838acdf)
Solutions
- Wait for the previous daemon process to fully exit (or `ipfs shutdown`) before starting a new one that serves HTTP.
- Check the node lifecycle before calling Serve/ServeWithReady; ensure the context passed into node construction is not already cancelled or near its deadline.
- If using kubo as a library, use context.WithCancel without a short deadline for long-running nodes and only cancel on intentional shutdown.
- Retry startup with backoff if this occurred during an automated restart race; the error is terminal for that attempt but the next attempt after teardown succeeds.
Example fix
// before: starts server even while shutting down
node, err := core.NewNode(ctx, cfg)
go httpServe(node)
cancel() // node context dies mid-startup
// after: ensure context outlives the server
node, err := core.NewNode(context.Background(), cfg)
if err != nil { return err }
go httpServe(node) // cancel only on shutdown, after servers exit Defensive patterns
Strategy: retry
Validate before calling
select {
case <-node.Context().Done():
// do not attempt to start the HTTP server
return node.Context().Err()
default:
// safe to call Serve/ServeWithReady
} Try / catch
err := corehttp.ServeWithReady(node, listener, opts)
if err != nil {
if strings.Contains(err.Error(), "process closing") || errors.Is(node.Context().Err(), context.Canceled) {
// shutdown race: tear down and retry after the node exits
return retryAfterTeardown()
}
return err
} Prevention
- Always check node.Context().Err() before starting HTTP servers on an embedded node.
- Use context.WithCancel (no short deadline) for long-running daemon node contexts.
- Serialize shutdown and startup: wait for the previous process to exit fully (pid check or lock) before relaunching.
- Add retry-with-backoff around daemon startup in supervisor scripts.
When it happens
Trigger: Calling ServeWithReady (directly or via Serve) with an IpfsNode whose Context() is already cancelled — e.g. the daemon received SIGINT/SIGTERM, `ipfs shutdown` was invoked, or the caller passed an already-cancelled context that the node context inherits, all while the HTTP server option chain is still being assembled.
Common situations: Daemon shutdown racing with gateway/API server startup; supervisory scripts that restart the daemon while an old process is still tearing down; programmatic kubo-as-a-library users whose node context expires before HTTP listeners start; slow initialization (large repo, migrations) combined with an external timeout cancelling the context.
Related errors
- closing plugins: %w
- daemon is shutting down (started %s ago)
- lcStatStop: stopFunc was nil
- %s close: %w
- repo is closed
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/b27af130be2ba160.
Report an issue: GitHub.