containerd/containerd · error
can't find container for TaskExit event: %w
Error message
can't find container for TaskExit event: %w
What it means
HandleEvent returns this when a TaskExit event's container ID cannot be retrieved from the CRI container store with an error other than NotFound. NotFound is treated as benign (container already forgotten); other errors indicate the store lookup itself failed. Note the ID (not ContainerID) is used to exclude exec-process exits.
Source
Thrown at internal/cri/server/events.go:338
// HandleEvent handles a containerd event.
func (ce *criEventHandler) HandleEvent(any any) error {
ctx := ctrdutil.NamespacedContext()
ctx, cancel := context.WithTimeout(ctx, handleEventTimeout)
defer cancel()
switch e := any.(type) {
case *eventtypes.TaskExit:
log.L.Infof("TaskExit event %+v", e)
// Use ID instead of ContainerID to rule out TaskExit event for exec.
cntr, err := ce.c.containerStore.Get(e.ID)
if err == nil {
if err := ce.c.handleContainerExit(ctx, e, cntr, cntr.SandboxID); err != nil {
return fmt.Errorf("failed to handle container TaskExit event: %w", err)
}
return nil
} else if !errdefs.IsNotFound(err) {
return fmt.Errorf("can't find container for TaskExit event: %w", err)
}
sb, err := ce.c.sandboxStore.Get(e.ID)
if err == nil {
if err := ce.c.handleSandboxExit(ctx, sb, e.ExitStatus, e.ExitedAt.AsTime()); err != nil {
return fmt.Errorf("failed to handle sandbox TaskExit event: %w", err)
}
return nil
} else if !errdefs.IsNotFound(err) {
return fmt.Errorf("can't find sandbox for TaskExit event: %w", err)
}
return nil
case *eventtypes.SandboxExit:
log.L.Infof("SandboxExit event %+v", e)
sb, err := ce.c.sandboxStore.Get(e.GetSandboxID())
if err == nil {
if err := ce.c.handleSandboxExit(ctx, sb, e.ExitStatus, e.ExitedAt.AsTime()); err != nil {
return fmt.Errorf("failed to handle sandbox TaskExit event: %w", err)
}View on GitHub (pinned to 4246446a2b)
Solutions
- Check containerd logs for metadata store errors at the same time
- Restart containerd to recover transient store failures
- Let event backoff retry the event after transient failure
- Run ctr c ls / crictl ps to verify store integrity after recovery
Defensive patterns
Strategy: try-catch
Validate before calling
// Go: treat NotFound as benign, everything else needs investigation:
cntr, err := containerStore.Get(e.ID)
if err != nil {
if errdefs.IsNotFound(err) {
return nil
}
return fmt.Errorf("can't find container for TaskExit event: %w", err)
} Type guard
func lookupFailed(err error) bool {
return err != nil && !errdefs.IsNotFound(err)
} Try / catch
if err := HandleEvent(ev); err != nil {
if lookupFailed(err) {
log.L.WithError(err).Errorf("store lookup failure; daemon health check needed")
monitor.Backoff(id, ev)
}
} Prevention
- Monitor bolt DB health and avoid IO starvation of the containerd root dir
- Verify store integrity after unclean containerd shutdowns
- Treat only NotFound as ignorable; retry other lookup failures via backoff
- Check for containerd version upgrade inconsistencies in store contents
When it happens
Trigger: containerStore.Get(e.ID) returns a non-NotFound error while dispatching *eventtypes.TaskExit — e.g. metadata DB failure, store corruption, or transport error.
Common situations: Bolt metadata DB contention or failure; containerd restart mid-event stream; corrupted container store after unclean shutdown.
Related errors
- failed to get container %s: %w
- sandbox %q not found: %w
- failed to handle container TaskExit event: %w
- can't find sandbox for TaskExit event: %w
- `mirrors` cannot be set when `config_path` is provided
AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02).
Data as JSON: /api/errors/ed1c94961acbd827.
Report an issue: GitHub.