Skip to content
Intellira
ArgoCDhigh severity

SyncFailed

When an ArgoCD sync fails, either the manifests never rendered or the apply errored. Read the operation message to find which, then the failing resource.

Written by Intellira Engineering, Editorial team

What Sync Failed means

Sync Failed is an operation result, not a comparison result. ArgoCD ran a sync — it tried to apply the desired state to the cluster — and the apply itself returned an error. Unlike OutOfSync, you cannot resolve this by just clicking sync again; the underlying error has to be fixed first.

But before you assume the apply failed, rule out the step before it: ArgoCD can only apply manifests it managed to generate. If the repo-server failed to render the manifests, you get a ComparisonError and there is no apply at all. The two failures live in different places and have different fixes:

  • ComparisonError → repo-server could not produce the desired state. Detail is in .status.conditions and the repo-server logs.
  • operation Failed → manifests rendered, the apply ran, the API server (or a hook, or a webhook) rejected something. Detail is in .status.operationState.

Diagnose it

argocd app get <app-name>
argocd app sync <app-name> --dry-run

Read the operation message and the per-resource status. The full detail is in the Application resource — and check conditions to separate a comparison failure from an apply failure:

# Apply-side detail
kubectl get application <app-name> -n argocd \
  -o jsonpath='{.status.operationState.message}'

# Comparison-side detail (manifest generation)
kubectl get application <app-name> -n argocd \
  -o jsonpath='{.status.conditions}'

kubectl get application <app-name> -n argocd -o yaml | less
# inspect .status.operationState, .status.resources[].message, .status.conditions

Typical apply errors that surface here:

# Immutable field
Deployment.apps "api" is invalid: spec.selector: field is immutable

# Missing CRD
unable to recognize "": no matches for kind "Rollout" in version "argoproj.io/v1alpha1"

# Admission webhook rejection
admission webhook "validate.kyverno.svc" denied the request: ...

# Failed PreSync hook
Job "db-migrate" failed: BackoffLimitExceeded

# Comparison failure (NOT an apply error — look in repo-server logs)
ComparisonError: Manifest generation error (cached): ...

If a hook failed, read the hook's Pod logs directly:

kubectl logs job/<hook-job> -n <namespace>

Caution

An immutable-field error (commonly spec.selector on a Deployment or the clusterIP on a Service) cannot be fixed by syncing. The existing resource must be deleted and recreated, or the manifest reverted to match the live immutable value. Plan this carefully — deleting a Service drops its ClusterIP.

Causes, each diagnosed and fixed

1. Manifest generation failed (ComparisonError)

The repo-server clones your repo and runs Helm/Kustomize/your plugin to produce manifests; a non-zero exit fails generation and the error is cached "to avoid runaway retries," so a stale message can persist after you push a fix (FAQ). A slow render fails with Context deadline exceeded.

  • Diagnose: .status.conditions shows ComparisonError. Search the repo-server logs for the app name: kubectl logs -n argocd deploy/argocd-repo-server | grep <app-name>.
  • Fix: correct the chart/overlay/values error and push. Then hard refresh to clear the cached error (argocd app get <app> --hard-refresh). For generation timeouts, raise --repo-server-timeout-seconds and scale up the repo-server (FAQ).

2. Immutable field rejected by the API server

A manifest changes a field Kubernetes forbids editing in place.

  • Diagnose: per-resource message reads field is immutable.
  • Fix: revert the manifest to the live value, or delete and recreate the resource deliberately (see the warning above). Replace=true as a sync option uses kubectl replace/create and can also recreate it (Sync Options).

3. Missing CRD

The custom resource is applied before (or without) its CustomResourceDefinition.

  • Diagnose: no matches for kind / the server could not find the requested resource.
  • Fix: install the CRD first. If the CRD ships in the same app, give it a lower argocd.argoproj.io/sync-wave so it applies first, or set the SkipDryRunOnMissingResource=true sync option so the dry-run does not fail on the unknown kind (Sync Options).

4. Admission / validation webhook denial

OPA Gatekeeper, Kyverno, or another ValidatingWebhookConfiguration rejects the resource.

  • Diagnose: admission webhook "..." denied the request.
  • Fix: the message names the policy — bring the manifest into compliance, or fix the policy. If the resource is a valid type the webhook mishandles, the Validate=false sync option skips kubectl validation (Sync Options).

5. PreSync / Sync / PostSync hook failed

Hooks run inside the sync operation. "If any PreSync fails, the entire sync stops"; a failed hook marks the whole operation Failed (Sync Phases and Waves).

  • Diagnose: message names a Job (e.g. db-migrate failed: BackoffLimitExceeded). Read its logs with kubectl logs job/<job>.
  • Fix: repair the underlying job (a broken migration, a missing secret), then re-sync. A SyncFail hook (argocd.argoproj.io/hook: SyncFail) can do cleanup when a sync fails.

6. Resource in a wave never goes Healthy (stalled phases)

ArgoCD applies waves in ascending order with a delay (default 2s, ARGOCD_SYNC_WAVE_DELAY) and assesses health between them; PostSync runs only "after Sync completes successfully and resources reach healthy status." If an early wave's resource stays unhealthy, later waves never run and the operation eventually fails or times out (Sync Phases and Waves).

  • Diagnose: the operation hangs in Progressing; the failing wave's resource is Degraded/Progressing in argocd app get. Note that Ingress, StatefulSet and SealedSecret have known health-check quirks that read as Progressing (FAQ).
  • Fix: make the stuck resource healthy (or correct a wrong sync-wave order); add a custom health check for types ArgoCD assesses incorrectly.

7. Shared resource owned by another Application

Two Applications manage the same object.

  • Diagnose: SharedResourceWarning. With the FailOnSharedResource=true sync option, ArgoCD "will fail the sync whenever it finds a resource ... already applied in the cluster by another Application" (Sync Options).
  • Fix: make exactly one Application own the resource; remove it from the other's source.

8. RBAC on the target namespace

The application-controller ServiceAccount lacks permission on the target namespace.

  • Diagnose: forbidden: User "system:serviceaccount:argocd:..." cannot create/patch ....
  • Fix: grant the controller the needed Role/RoleBinding in the target namespace.

Fix it

  1. Run argocd app get <app-name>. First check .status.conditions: a ComparisonError means manifests never rendered — fix it in the repo-server logs, not the cluster.
  2. Otherwise read .status.operationState and the per-resource message, then classify: immutable field, missing CRD, webhook denial, hook failure, stalled wave, shared resource, or RBAC.
  3. Apply the matching fix from the section above — each class has a different one.
  4. For a fixed ComparisonError, hard-refresh to clear the cached error before re-syncing.
  5. Re-run argocd app sync <app-name> only after the root error is addressed. Note an automated sync "will not reattempt against the same commit" after failure, so push a new commit or sync manually (Automated Sync).

A spec.syncPolicy.retry block (limit, backoff.duration, backoff.factor, backoff.maxDuration) retries transient apply failures with exponential backoff, but it will not fix a real error — it only buys time for flaky dependencies (Automated Sync).

How Intellira diagnoses this

Intellira reads the application's conditions and operationState from the ArgoCD MCP server and first decides which side failed — manifest generation (ComparisonError) or apply — instead of treating every red app the same. On the apply side it identifies the failing resource and the class of error: immutable field, missing CRD, webhook denial, stalled wave, shared resource, or RBAC. When a PreSync hook is at fault, it follows the chain into the hook Job's logs via the Kubernetes MCP server, so the answer is "the db-migrate PreSync Job exited non-zero because the migration failed," not a generic "sync failed." That is the difference between a status and a root cause.

Sources

By Intellira Engineering. AI-assisted draft, reviewed by the Intellira engineering team; claims cited inline; last verified 2026-06-02.

Frequently asked questions

Where does ArgoCD record why a sync failed?
In the application's operationState. argocd app get surfaces the message, and the full detail lives under .status.operationState in the Application resource. For per-resource errors, each resource's message field names the apply error returned by the Kubernetes API server. A ComparisonError instead lives in .status.conditions and points at the repo-server, not the API server.
Can a failing PreSync or PostSync hook fail the whole sync?
Yes. ArgoCD runs resource hooks as part of the sync operation. If a PreSync hook Job exits non-zero the whole sync is marked Failed and later phases do not run. Inspect the hook Job's logs with kubectl logs to see why it failed.
Why does the sync fail with "the server could not find the requested resource"?
A manifest references a CRD whose CustomResourceDefinition is not installed yet. Either install the CRD first, put it in an earlier sync-wave, or set the SkipDryRunOnMissingResource=true sync option so the dry-run does not fail on the unknown kind.
My app says ComparisonError but I never clicked sync — is that a sync failure?
No. ComparisonError means the repo-server could not render your manifests (Helm/Kustomize/plugin error or a generation timeout), so ArgoCD has no desired state to compare or apply. The fix is in the repo-server logs, not the target cluster.

Related errors

Find the root cause of SyncFailed on your stack

Connect read-only and Intellira correlates the change behind it across Bitbucket, Jenkins, ArgoCD and Kubernetes — with the evidence to prove it.