How to Troubleshoot Argo CD Sync Failures: A Complete Guide
Argo CD sync failures typically originate from five control-plane layers: the sync controller execution, sync-window policies, resource-permission validations, shared-resource conflicts, and misconfigured sync options.
When an Application resource fails to reach the Synced state in the argoproj/argo-cd repository, the failure propagates through a specific chain of validation checks within the application controller. Understanding the exact execution flow in controller/sync.go allows you to diagnose whether the issue stems from authentication, policy restrictions, or resource conflicts.
Understanding the Sync Controller Execution Flow
Every sync operation begins in controller/sync.go, where the application controller generates a unique operation ID and initializes a SyncContext.
Sync Context Generation and Error Handling
When a sync is triggered, the controller performs the following sequence:
- Generates a unique sync-ID using
syncid.Generate()for log correlation - Builds a
SyncContextvia the gitops-engine atgitops-engine/pkg/sync.NewSyncContext - Executes the actual apply/prune actions against the target cluster
If any step returns an error, the controller immediately sets OperationState to OperationError and propagates the message to the UI and CLI.
// controller/sync.go
syncId, err := syncid.Generate()
…
syncCtx, cleanup, err := sync.NewSyncContext(
compareResult.syncStatus.Revision,
reconciliationResult,
restConfig,
rawConfig,
m.kubectl,
app.Spec.Destination.Namespace,
opts...,
)
Key diagnostic step: Always retrieve the full operation state using argocd app get <app> -o yaml to inspect the status.operationState.message field associated with the syncId.
Sync Window Policy Validation
Before any resource comparison occurs, the controller validates whether the current time falls within an active sync window defined in the AppProject.
How Sync Windows Block Operations
The syncWindowPreventsSync() function evaluates the application's project-specific windows:
// controller/sync.go
func syncWindowPreventsSync(app *v1alpha1.Application, proj *v1alpha1.AppProject) (bool, error) {
window := proj.Spec.SyncWindows.Matches(app)
…
canSync, err := window.CanSync(isManual, operationStartTime)
…
return !canSync, nil
}
- Manual syncs only bypass windows if the specific window configuration permits manual actions
- Malformed window definitions return errors that block the sync entirely
Verify current window restrictions by running argocd proj get <project> and examining the syncWindows section.
Resource Permission and Destination Checks
Each resource undergoes validation against the project's allow/deny lists and the destination cluster's namespace policy through validateSyncPermissions().
Validating Against Project Allowlists
The controller checks every resource against the AppProject constraints:
// controller/sync.go
func validateSyncPermissions(project *v1alpha1.AppProject, destCluster *v1alpha1.Cluster,
getProjectClusters func(string) ([]*v1alpha1.Cluster, error), un *unstructured.Unstructured,
res *metav1.APIResource) error {
…
}
Permission failures generate errors like "resource X is not permitted in project Y". To resolve these, verify that the resource's group, kind, and target namespace appear in the project's allowlist using argocd proj allowlist <project>.
Shared Resource Detection and Conflicts
Argo CD detects when multiple Application resources manage the same Kubernetes object. By default, the controller issues a warning, but you can configure it to fail the sync explicitly.
When FailOnSharedResource=true is set in the sync options, the controller executes this blocking logic:
// controller/sync.go
hasSharedResource, sharedResourceMessage := hasSharedResourceCondition(app)
if syncOp.SyncOptions.HasOption("FailOnSharedResource=true") && hasSharedResource {
state.Phase = common.OperationFailed
state.Message = "Shared resource found: " + sharedResourceMessage
return
}
Check for shared resource conditions in the application status: argocd app get <app> -o yaml | grep -A5 "conditions".
Sync Options That Affect Failure Modes
Many sync failures stem from configuration options that modify how the controller applies resources. These options reside in spec.syncPolicy.syncOptions or as the argocd.argoproj.io/sync-options annotation.
| Option | Effect | Failure Mode |
|---|---|---|
Prune=false |
Skips resource deletion | Orphaned resources remain when they should be removed |
FailOnSharedResource=true |
Fails on ownership conflicts | Prevents overwriting resources managed by other apps |
RespectIgnoreDifferences=true |
Applies ignoreDifferences during apply | May mask configuration drift |
ServerSideApply=true |
Uses kubectl apply --server-side |
Can fail on field ownership conflicts with server-side apply |
CreateNamespace=true |
Auto-creates destination namespaces | Sync fails if namespace creation is forbidden |
Reference the full specification in [docs/user-guide/sync-options.md](https://github.com/argoproj/argo-cd/blob/master/docs/user-guide/sync-options.md).
Common Failure Signatures and Diagnostic Steps
Map symptoms to specific code paths using this diagnostic table:
| Symptom | Likely Cause | Verification Command |
|---|---|---|
| OperationError: "Failed to get destination cluster" | Cluster cache missing or authentication failure | argocd app get <app> -o yaml (check operationState) |
| Sync blocked by sync window | Current time outside allowed window | argocd proj get <proj> (review syncWindows) |
| Resource X is not permitted | Project denylist blocking group/kind | argocd proj get <proj> (check spec allow/deny) |
| Shared resource found | Ownership conflict with another app | argocd app get <app> -o yaml (check conditions) |
| Validation error | Resource cannot be client-side validated | Add Validate=false to sync options |
Step-by-Step Debugging Workflow
Follow this systematic approach to isolate the failure layer:
-
Inspect the application operation state
argocd app get <app> --output yamlExamine
status.operationState.phaseandstatus.operationState.messagefor the specific error returned bycontroller/sync.go. -
Correlate with controller logs
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controllerFilter by the
syncIdfrom step 1 to find the exact execution trace. -
Validate sync-window constraints
argocd proj get <project> -o yamlConfirm the
syncWindowsblock permits the current operation type (manual vs automated). -
Test permission configurations
argocd proj allowlist <project>Ensure the target namespace and resource types are explicitly permitted.
-
Execute a dry-run to isolate manifest errors
argocd app sync <app> --dry-runIf dry-run succeeds but live sync fails, investigate
Prune,ServerSideApply, orresource.customizationssettings. -
Adjust sync options to resolve the specific failure
argocd app set <app> --sync-option Prune=false argocd app set <app> --sync-option FailOnSharedResource=true
Summary
- Sync failures originate in
controller/sync.goduringSyncContextinitialization, permission validation, or window checking. - Sync windows defined in
AppProjectresources can block both automated and manual syncs unless explicitly configured to allow them. - Permission errors occur when
validateSyncPermissions()detects resources outside the project's allow/deny lists or unauthorized destination namespaces. - Shared resources trigger failures when
FailOnSharedResource=trueis set and anotherApplicationalready owns the object. - Diagnostic commands include
argocd app getfor operation state,argocd proj getfor policy validation, andkubectl logsfor controller-level debugging.
Frequently Asked Questions
What does "Shared resource found" error mean in Argo CD?
This error occurs when the FailOnSharedResource=true sync option is enabled and the controller detects that another Application resource already manages the same Kubernetes object. The controller sets state.Phase = common.OperationFailed in controller/sync.go and prevents the sync to avoid configuration drift. Resolve this by removing the duplicate resource definition from one application or disabling the strict ownership check if the shared management is intentional.
How do I bypass sync windows for manual operations?
Sync windows in AppProject specifications can restrict automated syncs to specific time ranges, but manual syncs require explicit window configuration to permit manual actions. The syncWindowPreventsSync() function checks window.CanSync(isManual, operationStartTime) where isManual indicates a user-triggered operation. If the window does not allow manual syncs, you must either wait for an active window or modify the AppProject to include manualSync: true in the window definition.
Why does my sync fail with "resource X is not permitted in project Y"?
This error originates from validateSyncPermissions() in controller/sync.go when a resource's group, kind, or target namespace violates the project's allow/deny lists. The controller validates every resource against the AppProject specification before applying it. Fix this by updating the project's allowlist using argocd proj allowlist <project> to include the resource's API group and the destination namespace, or by moving the resource to a permitted namespace.
How can I enable debug logging for sync operations?
To capture detailed syncId-prefixed logs from the sync controller, patch the argocd-application-controller deployment to set ARGOCD_LOG_LEVEL=debug. After applying the environment variable, trigger a sync and watch the logs with kubectl logs. The debug output includes the full SyncContext initialization sequence and detailed permission check results from controller/sync.go, allowing you to trace exactly where the operation fails.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →