containerd/containerd · warning

failed to stop stream server: %w

Error message

failed to stop stream server: %w

What it means

Returned by criService.Close when the CRI streaming server (exec stream exec/serve endpoints) fails to shut down cleanly. Close stops the event monitor and stats collector first, then stops the stream server; any error from streamServer.Stop() is wrapped with %w so the underlying cause (listener close failure, drain timeout) is preserved. It aborts the remainder of shutdown, so callers like Run report shutdown failure even though monitoring already stopped.

Source

Thrown at internal/cri/server/service.go:415

	}
	return nil
}

// Close stops the CRI service.
// TODO(random-liu): Make close synchronous.
func (c *criService) Close() error {
	log.L.Info("Stop CRI service")
	for name, h := range c.cniNetConfMonitor {
		if err := h.stop(); err != nil {
			log.L.WithError(err).Errorf("failed to stop cni network conf monitor for %s", name)
		}
	}
	c.eventMonitor.Stop()
	if c.statsCollector != nil {
		c.statsCollector.Stop()
	}
	if err := c.streamServer.Stop(); err != nil {
		return fmt.Errorf("failed to stop stream server: %w", err)
	}
	return nil
}

// IsInitialized indicates whether CRI service has finished initialization.
func (c *criService) IsInitialized() bool {
	return c.initialized.Load()
}

func (c *criService) introspectRuntimeHandler(ctx context.Context, intro introspection.Service, name string, r criconfig.Runtime) error {
	h := &runtime.RuntimeHandler{
		Name: name,
	}
	rawFeatures, err := introspectRuntimeFeatures(ctx, intro, r)
	if err != nil {
		log.G(ctx).WithError(err).Debugf("failed to introspect features of runtime %q", name)
	} else {
		h.Features = &runtime.RuntimeHandlerFeatures{}

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Inspect the wrapped cause (%w) to see why the stream server stop failed — usually a listener close error or drain timeout.
  2. Ensure all active exec/attach streams are terminated or given time to drain before shutting down containerd.
  3. If restarting containerd rapidly, verify the stream port (if configured) is released; adjust stream_server_address/port config.
  4. If it happens at process exit, treat it as non-fatal: containerd is exiting anyway; check for leaked goroutines in custom stream servers.
  5. Update containerd — several stream-server shutdown race fixes landed in later releases.

Example fix

// before: ignoring graceful drain on shutdown
ctr, _ := NewCRIService(...)
ctr.Close()
// after: stop streaming workloads first, then close
cancelStreams() // terminate active exec/attach sessions
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
<-streamsDrained(ctx)
if err := ctr.Close(); err != nil {
    log.Warnf("cri shutdown: %v", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// No pre-call check; ensure streams are drained before Close
if numActiveExecStreams() > 0 { waitForStreamsDrain(ctx) }

Try / catch

// Go: handle wrapped error at shutdown
if err := criService.Close(); err != nil {
    log.Warnf("CRI close failed: %v", err) // inspect errors.Unwrap for cause
}

Prevention

When it happens

Trigger: Calling Close() (via Run's defer during containerd shutdown or explicit Stop) when the stream server's underlying listener cannot be closed or its Serve goroutine returns an error (port already released unexpectedly, blocked connections, or a custom StreamServer whose Stop returns non-nil).

Common situations: containerd daemon shutdown/restart with active exec/attach/port-forward streams; CI teardown races where streams are still being drained; custom streamer implementations returning errors from Stop; port conflicts after rapid restart of containerd.

Related errors


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/38a04f24cf8c2219. Report an issue: GitHub.