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

  1. Wait for the previous daemon process to fully exit (or `ipfs shutdown`) before starting a new one that serves HTTP.
  2. Check the node lifecycle before calling Serve/ServeWithReady; ensure the context passed into node construction is not already cancelled or near its deadline.
  3. If using kubo as a library, use context.WithCancel without a short deadline for long-running nodes and only cancel on intentional shutdown.
  4. 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

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


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/b27af130be2ba160. Report an issue: GitHub.