ipfs/kubo · warning

%s close: %w

Error message

%s close: %w

What it means

CloseWithCtx runs a subsystem's Close in a goroutine and waits up to the caller's context deadline; if the subsystem does not finish closing in time, it logs and returns '<name> close: <ctx.Err()>' (usually context.DeadlineExceeded). The subsystem may still be closing in the background.

Source

Thrown at core/shutdown/close.go:29

var closeLog = logging.Logger("shutdown")

// CloseWithCtx runs close in a goroutine and returns when it finishes or
// when ctx is done, whichever comes first. If ctx fires before close
// returns, the goroutine is leaked intentionally; the process is about to
// exit, so the leak is bounded by process lifetime. Logs at ERROR which
// subsystem failed to close in time so operators see it in journal/docker
// logs.
func CloseWithCtx(ctx context.Context, name string, close func() error) error {
	done := make(chan error, 1)
	start := time.Now()
	go func() { done <- close() }()
	select {
	case err := <-done:
		return err
	case <-ctx.Done():
		closeLog.Errorf("subsystem %q failed to close within shutdown deadline (after %s): %s",
			name, time.Since(start), ctx.Err())
		return fmt.Errorf("%s close: %w", name, ctx.Err())
	}
}

View on GitHub (pinned to 329838acdf)

Solutions

  1. Increase the shutdown deadline context (e.g. 30s) given to CloseWithCtx
  2. Check shutdown logs for the named subsystem to find what is blocking its Close
  3. Ensure the subsystem's dependencies are closed in correct order (children before parents)
  4. Gracefully stop active operations (downloads, GC, FUSE unmounts) before shutdown

Example fix

// before
closeCtx, cancel := context.WithTimeout(ctx, time.Second)
// after
closeCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
Defensive patterns

Strategy: try-catch

Validate before calling

// nothing to validate beforehand; ensure subsystems are idle before closing
if node.IsBusy() { time.Sleep(...) }

Try / catch

closeCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := CloseWithCtx(closeCtx, node, "node"); err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        log.Warn("node close exceeded deadline; may still be flushing")
    }
}

Prevention

When it happens

Trigger: Calling CloseWithCtx with a too-short context while a subsystem (e.g. datastore, blockservice, libp2p host) hangs in Close — open handles, slow flushes, stuck network calls.

Common situations: Daemon shutdown timing out because a FUSE mount or datastore flush blocks; tests or scripts using a 1-2 second shutdown timeout on a node with large datastore GC.

Related errors


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