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:

  1. Generates a unique sync-ID using syncid.Generate() for log correlation
  2. Builds a SyncContext via the gitops-engine at gitops-engine/pkg/sync.NewSyncContext
  3. 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:

  1. Inspect the application operation state

    argocd app get <app> --output yaml

    Examine status.operationState.phase and status.operationState.message for the specific error returned by controller/sync.go.

  2. Correlate with controller logs

    kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller

    Filter by the syncId from step 1 to find the exact execution trace.

  3. Validate sync-window constraints

    argocd proj get <project> -o yaml

    Confirm the syncWindows block permits the current operation type (manual vs automated).

  4. Test permission configurations

    argocd proj allowlist <project>

    Ensure the target namespace and resource types are explicitly permitted.

  5. Execute a dry-run to isolate manifest errors

    argocd app sync <app> --dry-run

    If dry-run succeeds but live sync fails, investigate Prune, ServerSideApply, or resource.customizations settings.

  6. 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.go during SyncContext initialization, permission validation, or window checking.
  • Sync windows defined in AppProject resources 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=true is set and another Application already owns the object.
  • Diagnostic commands include argocd app get for operation state, argocd proj get for policy validation, and kubectl logs for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →