IL-0058 Argo CD users · 2026-07-08 (published)kubernetesgcp
A user running Argo CD v3.3.9 (Helm chart argo-cd-9.5.11) on GKE 1.30.x, with the application-controller as a sharded Deployment with several replicas, reported that shared namespaces were deleted after a rolling restart of the controller. The cluster had a large number of resources (the reproduction mentions 20k+ ReplicaSets), and several applications declared the same namespace.
The report first blamed timing: the controller seemed to process syncs before its cluster cache had finished the initial LIST, so it misjudged who owned the shared namespace and either pruned it through automated sync or force-replaced it through a manual sync. The namespaces entered Terminating and about 97 applications were stuck Missing/OutOfSync.
A commenter who read the controller and gitops-engine code found the automated prune path guarded and the cache locked while it repopulates. The real gap was the manual sync with force: true: when two applications declare the same resource and the one that does not own the app.kubernetes.io/instance tracking label is force-synced, Argo CD replaces the resource with kubectl replace --force, deleting and recreating it, regardless of that application's Prune=false setting. That can happen any time, not only after a restart. The reporter agreed, closed this issue and refiled the problem as #29739.
After a rolling restart of the Argo CD application-controller, namespaces that several applications shared went into the Terminating state. About 97 applications were then stuck in a Missing/OutOfSync deadlock.
The controller logs carried two warnings. At 16:59:58Z: "Detected changes to resource Namespace/foo which is currently being deleted". At 17:00:10Z, about three minutes after the restart: "Failed to list *unstructured.Unstructured using watch: very old resource version: ... continue parameter too old" for type=ReplicaSet.apps. The reporter read the second message as proof that the controller's initial LIST of cluster resources was still running when the deletions happened, and noted that the "Cluster cache informer synced" log line appears when the watch starts, not when the initial LIST completes.
The reporter saw two deletion paths at the same time. Automated sync with prune: true resumed right after the restart and, the reporter believed, pruned a namespace it wrongly saw as orphaned. Separately, someone ran a manual bulk sync with force: true from the UI; the shared namespace's app.kubernetes.io/instance label pointed to another application, and Argo CD issued kubectl replace --force, which deleted the namespace and recreated it. The initiatedBy field in the controller logs confirmed the manual sync. The annotation argocd.argoproj.io/sync-options: Prune=false did not stop the force replace.
A later reading of the controller code, which the reporter accepted, found the automated path guarded: auto-sync runs only when an application is already OutOfSync, a failed live-state load marks it Unknown, and the cluster cache holds a lock while it repopulates. The manual force sync is not behind that check, so the deletion can happen at any time, not only after a restart.
Symptom ? Resources deleted
Root cause ? Software defect
–
–
The sync option Prune=false only protects the prune path; it does not stop a force sync from deleting and recreating a resource. When two Argo CD applications declare the same namespace or other cluster-scoped resource, force-syncing the application that does not own the tracking label can delete it at any time.
same root cause · shared Argo CD