docker/cli · error
failed to wait on tasks of stack
Error message
failed to wait on tasks of stack: %s: %w
What it means
Raised during `docker stack rm` (when not detached) if waitOnTasks fails for a stack. The wrapped %w carries the error from waitOnTasks (which itself wraps getStackTasks failures). It indicates removal did not complete cleanly within the wait loop, usually because task listing or task termination could not be observed.
Solutions
- Read the wrapped error to see whether it's an API/RPC failure or a task-listing issue.
- Re-run `docker stack rm <stack>` (the resources still pending will be reprocessed).
- Check manager health with `docker node ls` and that the node is reachable.
- If tasks are stuck, inspect them with `docker stack ps <stack>` and force-remove obstructing services.
- Use `--detach` to skip the wait if removal was issued but convergence can't be observed.
Example fix
// before: blocking remove fails waiting on tasks docker stack rm mystack // after: detach from the wait, then verify docker stack rm --detach mystack docker stack ps mystack # should eventually show nothing
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check manager reachability before a blocking remove
info, err := c.Info(ctx, client.InfoOptions{})
if err != nil { return fmt.Errorf("manager unreachable: %w", err) }
if !info.Info.Swarm.ControlAvailable { return errors.New("not a swarm manager") } Try / catch
// Retry stack rm on transient wait failures; fall back to --detach
for attempt := 0; attempt < 3; attempt++ {
if err := stackRm(ctx, cli, opts); err == nil {
return nil
} else if !strings.Contains(err.Error(), "failed to wait on tasks") {
return err
}
}
// last resort: detach from the wait
opts.detach = true
return stackRm(ctx, cli, opts) Prevention
- Confirm manager health before a blocking remove.
- Fall back to --detach if the wait can't be observed.
- Re-run rm after stabilizing the cluster; it is safe to re-invoke.
When it happens
Trigger: Running `docker stack rm <stack>` without --detach; after services/networks/etc. are removed, waitOnTasks at remove.go:90 polls getStackTasks until all tasks reach a terminal state, and if that poll fails (e.g. RPC error to the manager) the error is wrapped at remove.go:92.
Common situations: Manager node becoming unreachable mid-removal; the wait loop hitting an API error; tasks stuck in a non-terminal state due to node loss; the daemon returning errors during TaskList while resources are being torn down.
Related errors
- failed to remove some resources from stack
- %s: %w
- nothing found in stack
- this node is not a swarm manager. Use "docker swarm init"…
- cannot get label com.docker.stack.namespace for service
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/948346a1a1fdd7ea.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/stack/remove.go:92
_, _ = fmt.Fprintln(dockerCli.Err(), "Nothing found in stack:", namespace)
continue
}
// TODO(thaJeztah): change this "hasError" boolean to return a (multi-)error for each of these functions instead.
hasError := removeServices(ctx, dockerCli, services.Items)
hasError = removeSecrets(ctx, dockerCli, secrets.Items) || hasError
hasError = removeConfigs(ctx, dockerCli, configs.Items) || hasError
hasError = removeNetworks(ctx, dockerCli, networks.Items) || hasError
if hasError {
errs = append(errs, errors.New("failed to remove some resources from stack: "+namespace))
continue
}
if !opts.detach {
err = waitOnTasks(ctx, apiClient, namespace)
if err != nil {
errs = append(errs, fmt.Errorf("failed to wait on tasks of stack: %s: %w", namespace, err))
}
}
}
return errors.Join(errs...)
}
func sortServiceByName(services []swarm.Service) func(i, j int) bool {
return func(i, j int) bool {
return services[i].Spec.Name < services[j].Spec.Name
}
}
func removeServices(ctx context.Context, dockerCLI command.Cli, services []swarm.Service) bool {
var hasError bool
sort.Slice(services, sortServiceByName(services))
for _, service := range services {
_, _ = fmt.Fprintln(dockerCLI.Out(), "Removing service", service.Spec.Name)
if _, err := dockerCLI.Client().ServiceRemove(ctx, service.ID, client.ServiceRemoveOptions{}); err != nil {View on GitHub (pinned to 4f84911bfe)