{"record":{"id":"948346a1a1fdd7ea","repo":"docker/cli","slug":"failed-to-wait-on-tasks-of-stack-s-w","errorCode":null,"errorMessage":"failed to wait on tasks of stack: %s: %w","messagePattern":"failed to wait on tasks of stack: (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/stack/remove.go","lineNumber":92,"sourceCode":"\t\t\t_, _ = fmt.Fprintln(dockerCli.Err(), \"Nothing found in stack:\", namespace)\n\t\t\tcontinue\n\t\t}\n\n\t\t// TODO(thaJeztah): change this \"hasError\" boolean to return a (multi-)error for each of these functions instead.\n\t\thasError := removeServices(ctx, dockerCli, services.Items)\n\t\thasError = removeSecrets(ctx, dockerCli, secrets.Items) || hasError\n\t\thasError = removeConfigs(ctx, dockerCli, configs.Items) || hasError\n\t\thasError = removeNetworks(ctx, dockerCli, networks.Items) || hasError\n\n\t\tif hasError {\n\t\t\terrs = append(errs, errors.New(\"failed to remove some resources from stack: \"+namespace))\n\t\t\tcontinue\n\t\t}\n\n\t\tif !opts.detach {\n\t\t\terr = waitOnTasks(ctx, apiClient, namespace)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, fmt.Errorf(\"failed to wait on tasks of stack: %s: %w\", namespace, err))\n\t\t\t}\n\t\t}\n\t}\n\treturn errors.Join(errs...)\n}\n\nfunc sortServiceByName(services []swarm.Service) func(i, j int) bool {\n\treturn func(i, j int) bool {\n\t\treturn services[i].Spec.Name < services[j].Spec.Name\n\t}\n}\n\nfunc removeServices(ctx context.Context, dockerCLI command.Cli, services []swarm.Service) bool {\n\tvar hasError bool\n\tsort.Slice(services, sortServiceByName(services))\n\tfor _, service := range services {\n\t\t_, _ = fmt.Fprintln(dockerCLI.Out(), \"Removing service\", service.Spec.Name)\n\t\tif _, err := dockerCLI.Client().ServiceRemove(ctx, service.ID, client.ServiceRemoveOptions{}); err != nil {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/command/stack/remove.go#L74-L110","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: blocking remove fails waiting on tasks\ndocker stack rm mystack\n// after: detach from the wait, then verify\ndocker stack rm --detach mystack\ndocker stack ps mystack  # should eventually show nothing","handlingStrategy":"retry","validationCode":"// Pre-check manager reachability before a blocking remove\ninfo, err := c.Info(ctx, client.InfoOptions{})\nif err != nil { return fmt.Errorf(\"manager unreachable: %w\", err) }\nif !info.Info.Swarm.ControlAvailable { return errors.New(\"not a swarm manager\") }","typeGuard":null,"tryCatchPattern":"// Retry stack rm on transient wait failures; fall back to --detach\nfor attempt := 0; attempt < 3; attempt++ {\n    if err := stackRm(ctx, cli, opts); err == nil {\n        return nil\n    } else if !strings.Contains(err.Error(), \"failed to wait on tasks\") {\n        return err\n    }\n}\n// last resort: detach from the wait\nopts.detach = true\nreturn stackRm(ctx, cli, opts)","preventionTips":["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."],"tags":["docker","stack","remove","tasks","wait","swarm"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}