Argo CD Application Stuck Syncing Because a Pre-Sync or Post-Sync Hook Never Completes
Application stuck in Syncing with Operation Phase Running because a pre-sync or post-sync hook never completes. Diagnose with operation state, hook pod labels, logs and annotations, then cancel the operation before fixing the source of truth in Git.
14 Nov 2025, 21:06 UTC

Recognizable condition
An Argo CD Application stays in Sync Status Syncing with Operation Phase Running for an abnormal duration. The UI and CLI show an active hook operation, and the application resources remain in the pre-sync state while the sync is blocked. The hook is typically a Job or Pod created in the target namespace with labels argocd.argoproj.io/hook.
The useful takeaway is to cancel the stuck operation before fixing the hook source. Editing the hook manifest while the operation is still Running can be overwritten on the next reconcile and leaves finalizers on the Application.
Cause diagnostic table
| Cause | Symptom in Argo CD | Where to see it |
|---|---|---|
| Hook pod never created | Operation Phase Running, no hook resource in target namespace | Application operationState, kubectl get pods -n <namespace> -l argocd.argoproj.io/hook |
| Missing ServiceAccount or RBAC for hook namespace | Pod stays Pending with events about serviceaccount or permission denied | kubectl describe pod, kubectl get events -n <namespace> |
| CrashLoopBackOff from invalid manifest or image pull error | Hook pod restarts repeatedly, operation never advances | Pod phase CrashLoopBackOff, pod logs |
| Hook script does not exit zero | Pod stays Running, logs show script active with no completion | Pod logs, age growing |
| Hook exceeds timeout and is terminated | Operation Phase Running then Error, hook pod deleted by controller | Application status.operationState, Argo CD controller logs |
| Annotation typo or wrong hook type | Argo CD waits indefinitely for a completion event that will never arrive | Manifest annotations argocd.argoproj.io/hook and argocd.argoproj.io/hook-delete-policy |
Ordered checks
Confirm the blocking operation. Run from a workstation with Argo CD CLI credentials:
argo app get <app-name> -o yamlRequired permission: read Application resource in Argo CD control plane. Check status.operationState.phase and status.operationState.syncResult.hooks for the hook name and phase. Risk: none, read-only.
List hook pods or jobs in the target namespace. Run in the cluster with kubectl access to the target namespace:
kubectl get pods -n <namespace> -l argocd.argoproj.io/hook=<hook-name> -o wideCheck phase, restarts, and age. A missing pod points to creation failure. A Running pod with growing age points to a hanging script.
Inspect pod logs and events.
kubectl logs -n <namespace> <pod-name> --tail=200kubectl describe pod -n <namespace> <pod-name>Look for script errors, permission denials, or image pull failures. Risk: logs may contain sensitive data.
Verify hook annotations and manifest. In the Git source of truth, confirm the resource has a correctly spelled annotation such as argocd.argoproj.io/hook: PreSync or PostSync and optionally argocd.argoproj.io/hook-delete-policy.
Typo in the annotation causes Argo CD to treat the resource as a normal object and wait for a hook completion event that never comes.
Review Application syncOptions and timeout settings. Check the Application spec for syncOptions and any hook timeout overrides. Hook timeout defaults are version sensitive in Argo CD 2.x.
Fixes tied to findings
Cancel the stuck operation first. From the CLI:
argo app cancel-operation <app-name>Required permission: update Application in Argo CD. Do not force delete hook pods before cancelling, as orphaned finalizers can leave the Application stuck.
- Pod missing. Fix ServiceAccount and RBAC for the hook namespace so Argo CD can create the hook pod. Ensure the hook ServiceAccount exists and can create pods. Re-apply the corrected manifest in Git and trigger a sync.
- CrashLoopBackOff. Correct the container image or manifest error in Git, then let Argo CD reconcile. Manual edits to live hook resources will be overwritten.
- Script hangs. Update the hook to exit with zero on success. If the work legitimately needs more time, increase the timeout via an annotation or Application syncOptions. Reduce hook work where possible.
- Timeout hit. Raise the hook timeout or reduce hook work, then re-sync. Verify the change in Git before re-triggering.
- Annotation error. Correct the annotation spelling and hook type in Git, then re-sync.
Verification
After a fix, re-trigger a sync and observe operation phase transitions to Succeeded and Application health returning to expected state. Run:
argo app get <app-name>Confirm status.operationState.phase is Succeeded and no hook resources remain in Running state in the target namespace.
Escalation criteria
- Hook pod remains Running beyond 2x configured timeout with no log progress.
- Repeated hook failures across multiple applications indicating a controller or cluster issue.
- Hook requires privileged operations or external system access beyond normal cluster scope.
- Operation state does not advance after hook completion, suggesting a controller bug.
Limitations
This guide assumes Argo CD 2.x with resource hooks using annotations and a dedicated hook ServiceAccount in the target namespace. Label names and timeout defaults are version sensitive. Manual edits to live hook resources are transient; fix the source of truth in Git and let Argo CD reconcile.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.