containerd/containerd · error

failed to add sandbox %+v into store: %w

Error message

failed to add sandbox %+v into store: %w

What it means

RunPodSandbox adds the fully started sandbox object into the persistent sandbox store (in INIT state). Failure here means the store Add rejected the entry (e.g. it already exists), and the running sandbox will be rolled back by the deferred cleanup.

Source

Thrown at internal/cri/server/sandbox_run.go:387

	defer func() {
		if retErr != nil {
			deferCtx, deferCancel := util.DeferContext()
			defer deferCancel()
			c.nri.RemovePodSandbox(deferCtx, &sandbox)
		}
	}()

	if err := sandbox.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
		// Set the pod sandbox as ready after successfully start sandbox container.
		status.State = sandboxstore.StateReady
		return status, nil
	}); err != nil {
		return nil, fmt.Errorf("failed to update sandbox status: %w", err)
	}

	// Add sandbox into sandbox store in INIT state.
	if err := c.sandboxStore.Add(sandbox); err != nil {
		return nil, fmt.Errorf("failed to add sandbox %+v into store: %w", sandbox, err)
	}
	// We no longer need to stop sandbox with a cleanup defer since it is in the store.
	rollbackSandbox = false

	// Send CONTAINER_CREATED event with both ContainerId and SandboxId equal to SandboxId.
	// Note that this has to be done after sandboxStore.Add() because we need to get
	// SandboxStatus from the store and include it in the event.
	c.generateAndSendContainerEvent(ctx, id, id, runtime.ContainerEventType_CONTAINER_CREATED_EVENT)

	exitCh, err := c.sandboxService.WaitSandbox(util.NamespacedContext(), sandbox.Sandboxer, id)
	if err != nil {
		return nil, fmt.Errorf("failed to wait sandbox %s: %v", id, err)
	}

	// start the monitor after adding sandbox into the store, this ensures
	// that sandbox is in the store, when event monitor receives the TaskExit event.
	//
	// TaskOOM from containerd may come before sandbox is added to store,

View on GitHub (pinned to 4246446a2b)

Solutions

  1. If the error is already-exists, call RemovePodSandbox for the stale sandbox ID (or crictl stopp/rmp) and retry.
  2. Use a fresh sandbox UID for retries (kubelet normally generates a new one per attempt).
  3. Check metadata DB health (disk space, bolt errors) if Add fails on a clean ID.
  4. Restart containerd to clear inconsistent in-memory state if stale entries persist.

Example fix

// before: blind retry
// after: clear stale sandbox then retry
_, err := r.RunPodSandbox(ctx, req)
if err != nil && strings.Contains(err.Error(), "already exists") {
    _ = r.RemovePodSandbox(ctx, &runtime.RemovePodSandboxRequest{PodSandboxId: sandboxID})
    return r.RunPodSandbox(ctx, req)
}
Defensive patterns

Strategy: validation

Validate before calling

// before RunPodSandbox with a reused ID, clear stale state
// crictl stopp <id> ; crictl rmp <id>
// or in Go: pods, _ := client.ListPodSandbox(ctx, &runtime.PodSandboxFilter{Id: sandboxID})

Try / catch

if err != nil && strings.Contains(err.Error(), "already exists") {
    _ = client.RemovePodSandbox(ctx, &runtime.RemovePodSandboxRequest{PodSandboxId: id})
    return client.RunPodSandbox(ctx, req)
}

Prevention

When it happens

Trigger: c.sandboxStore.Add(sandbox) returns an error, most commonly ErrAlreadyExists when a sandbox with the same ID is already in the store, or a metadata persistence failure.

Common situations: Retried RunPodSandbox after a previous partial success left a stale entry; kubelet retrying with a reused sandbox ID; underlying metadata DB write errors.

Related errors


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