From d113e117d8a99d40c0a3eb68674e3d5b5ef8a0d9 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Tue, 8 Sep 2026 21:14:21 -0700 Subject: [PATCH] sandbox: tolerate a shim exiting during ShutdownSandbox A sandbox shim may exit as soon as it has shut its sandbox down, before containerd reads the ShutdownSandbox response, in which case the client sees ttrpc.ErrClosed. The task Shutdown RPC has the same race and shimTask.Shutdown treats a closed connection as a successful shutdown; controllerLocal.Shutdown returned it as an error instead, failing RemovePodSandbox for a sandbox that was already gone. Signed-off-by: Maksym Pavlenko --- plugins/sandbox/controller.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/sandbox/controller.go b/plugins/sandbox/controller.go index 52fa8caf54..1d237a2560 100644 --- a/plugins/sandbox/controller.go +++ b/plugins/sandbox/controller.go @@ -18,6 +18,7 @@ package sandbox import ( "context" + "errors" "fmt" "os" "time" @@ -27,6 +28,7 @@ import ( "github.com/containerd/log" "github.com/containerd/plugin" "github.com/containerd/plugin/registry" + "github.com/containerd/ttrpc" "github.com/containerd/typeurl/v2" imagespec "github.com/opencontainers/image-spec/specs-go/v1" @@ -260,8 +262,11 @@ func (c *controllerLocal) Shutdown(ctx context.Context, sandboxID string) error return err } + // A shim may exit as soon as it has shut its sandbox down, before the + // response is read; a closed connection means the shutdown happened, as + // for the task Shutdown RPC. _, err = svc.ShutdownSandbox(ctx, &runtimeAPI.ShutdownSandboxRequest{SandboxID: sandboxID}) - if err != nil { + if err != nil && !errors.Is(err, ttrpc.ErrClosed) { return fmt.Errorf("failed to shutdown sandbox: %w", errgrpc.ToNative(err)) }