GoogleContainerTools/skaffold · error

pruning removed container: %w

Error message

pruning removed container: %w

What it means

localDaemon.Delete removes a container and then prunes to clean up, returning "pruning removed container: %w" if apiClient.ContainerPrune fails. Note the container removal itself only logs a warning; it is the prune step that surfaces as a hard error. This wraps daemon/transport failures during the prune API call.

Source

Thrown at pkg/skaffold/docker/image.go:223

	}
}

func (l *localDaemon) ContainerExists(ctx context.Context, name string) bool {
	_, err := l.apiClient.ContainerInspect(ctx, name, client.ContainerInspectOptions{})
	return err == nil
}

// Delete stops, removes, and prunes a running container
func (l *localDaemon) Delete(ctx context.Context, out io.Writer, id string) error {
	if _, err := l.apiClient.ContainerStop(ctx, id, client.ContainerStopOptions{}); err != nil {
		log.Entry(ctx).Debugf("unable to stop running container: %s", err.Error())
	}
	if _, err := l.apiClient.ContainerRemove(ctx, id, client.ContainerRemoveOptions{}); err != nil {
		log.Entry(ctx).Warnf("unable to remove container: %s", err.Error())
	}
	_, err := l.apiClient.ContainerPrune(ctx, client.ContainerPruneOptions{})
	if err != nil {
		return fmt.Errorf("pruning removed container: %w", err)
	}
	return nil
}

// Run creates a container from a given image reference, and returns a wait channel and the container ID.
func (l *localDaemon) Run(ctx context.Context, out io.Writer, opts ContainerCreateOpts) (<-chan container.WaitResponse, <-chan error, string, error) {
	if opts.ContainerConfig == nil {
		return nil, nil, "", fmt.Errorf("cannot call Run with empty container config")
	}
	c, err := l.apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{
		Config: opts.ContainerConfig,
		HostConfig: &container.HostConfig{
			NetworkMode:  container.NetworkMode(opts.Network),
			VolumesFrom:  opts.VolumesFrom,
			PortBindings: opts.Bindings,
			Mounts:       opts.Mounts,
		},
		Name: opts.Name,

View on GitHub (pinned to a1189de023)

Solutions

  1. Verify the daemon is healthy with `docker info`; restart Docker Desktop/daemon if it is wedged, then retry the delete.
  2. Check the context/deadline: rerun without cancellation or increase timeout if the prune timed out.
  3. For remote DOCKER_HOST, confirm connectivity (ssh tunnel / tcp port) and retry.
  4. Treat as cleanup-best-effort: confirm the container itself is gone (`docker ps -a`); the prune failure usually only affects reclaiming stopped containers/networks.

Example fix

// before: delete fails at prune when daemon is restarting
if _, err := l.apiClient.ContainerPrune(ctx, client.ContainerPruneOptions{}); err != nil {
    return fmt.Errorf("pruning removed container: %w", err)
}

// after (caller-side resilience): log prune failure instead of failing cleanup
if _, err := l.apiClient.ContainerPrune(ctx, client.ContainerPruneOptions{}); err != nil {
    log.Entry(ctx).Warnf("unable to prune after container removal: %s", err.Error())
}
return nil
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: check daemon reachability before Delete cleanup
cli, _ := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if _, err := cli.Ping(context.Background()); err != nil {
    log.Warnf("docker daemon unreachable, skipping container cleanup: %v", err)
    return nil
}

Try / catch

if err := daemon.Delete(ctx, id); err != nil {
    // cleanup is best-effort: the container may already be removed
    if strings.Contains(err.Error(), "pruning removed container") {
        log.Warnf("post-removal prune failed (container itself likely gone): %v", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling Delete(ctx, id) (the LocalDaemon interface method) when ContainerPrune returns an error: Docker daemon stopped/restarting mid-call, context cancelled or timed out, daemon returned 500, or network connection to a remote DOCKER_HOST dropped during the prune request.

Common situations: Skaffold dev cleanup while Docker Desktop is restarting; long-running prune hitting the daemon's request timeout; remote Docker (ssh/tcp DOCKER_HOST) connection reset; daemon disk pressure causing prune API failure.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/c2a5bcffd505a5f3. Report an issue: GitHub.