Repository navigation
Auto-sync skipped for multi-source apps whose path source also sets ref (regression from #25531, 3.4.0) #30558
Description
Checklist:
- I've searched in the docs and FAQ for my answer: https://bit.ly/argocd-faq.
- I've included steps to reproduce the bug.
- I've pasted the output of
argocd version.
Describe the bug
If every source of a multi-source Application sets ref, including the source that has path and renders the manifests, Argo CD 3.4 no longer auto-syncs new commits. The app turns OutOfSync and stays there until it is synced manually. selfHeal is not enabled. No manifest-generate-paths annotation is set.
A source with both path and ref is documented and supported (multiple_sources.md, v3.4.12, L81-L89):
If the
pathfield is set in the$valuessource, Argo CD will attempt to generate resources from the git repository at that URL. If thepathfield is not set, Argo CD will use the repository solely as a source of value files.
Even when the
reffield is configured with thepathfield,$valuestill represents the root of sources with thereffield. Consequently,valueFilesmust be specified as relative paths from the root of sources.
We use this pattern for app-of-apps Applications whose chart reads value files from its own repository. These apps were auto-synced on 3.3.6; after upgrading to 3.4.12, every new commit has to be synced by hand.
Root cause
ApplicationSource.IsRef() returns Ref != "", so a source with path + ref counts as a ref source.
-
3.3.6:
GetRepoObjssetsrevisionsMayHaveChanges = truefor every source that is not checked throughUpdateRevisionForPaths(controller/state.go L296-L299). -
3.4.0: fix: manifest-generate-paths for multisource git+helm #25531 (fix for
manifest-generate-pathsdoes not work in the case of multisource helm+git #25529) changed this branch toelse if !source.IsRef()(v3.4.12 controller/state.go L291-L294). -
3.5: the logic moved to
evaluateRevisionChanges, and the assumption is written down (v3.5.6 controller/state.go L441-L445):if source.IsRef() { // For ref source specifically, we always return false since their change are evaluated as part of the source // referencing them. return revision, false, nil }
That assumption holds for a ref source that only provides value files. It does not hold for a ref source that also renders manifests. When every source sets ref, no source evaluates changes, so revisionsMayHaveChanges stays false. autoSync passes it to alreadyAttemptedSync as newRevisionHasChanges. With false, the function skips the revision comparison and only compares spec.sources with status.operationState.syncResult.sources (v3.4.12 controller/appcontroller.go L2486-L2505). The sources have not changed, so the sync counts as already attempted and is skipped (L2398-L2400). alreadyAttemptedSync is identical in 3.3.6, 3.4.12 and 3.5.6.
To Reproduce
Verified on 3.4.12 with a single Git repository and branch:
repro/sre-2567/chart/Chart.yaml
repro/sre-2567/chart/templates/configmap.yaml
repro/sre-2567/base-values.yaml # base: from-chart-repo
repro/sre-2567/values.yaml # counter: "1"
# Chart.yaml
apiVersion: v2
name: repro
version: 0.1.0
---
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}
data:
base: {{ .Values.base | quote }}
counter: {{ .Values.counter | quote }}Two Applications that differ only in where ref: chartrepo is set:
# Affected: the path source also sets `ref`
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: sre-2567-path-and-ref
namespace: argocd
spec:
project: default
destination: {server: https://kubernetes.default.svc, namespace: sre-2567-repro}
syncPolicy:
automated: {enabled: true}
syncOptions: [CreateNamespace=true]
sources:
- repoURL: <repo>
targetRevision: <branch>
path: repro/sre-2567/chart
ref: chartrepo
helm:
releaseName: path-and-ref
valueFiles:
- $chartrepo/repro/sre-2567/base-values.yaml
- $values/repro/sre-2567/values.yaml
- repoURL: <repo>
targetRevision: <branch>
ref: values
---
# Control: `ref: chartrepo` moved to a separate ref-only source
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: sre-2567-split-ref
namespace: argocd
spec:
project: default
destination: {server: https://kubernetes.default.svc, namespace: sre-2567-repro}
syncPolicy:
automated: {enabled: true}
syncOptions: [CreateNamespace=true]
sources:
- repoURL: <repo>
targetRevision: <branch>
path: repro/sre-2567/chart
helm:
releaseName: split-ref
valueFiles:
- $chartrepo/repro/sre-2567/base-values.yaml
- $values/repro/sre-2567/values.yaml
- repoURL: <repo>
targetRevision: <branch>
ref: values
- repoURL: <repo>
targetRevision: <branch>
ref: chartrepo-
Apply both Applications. Both auto-sync the initial commit (
cc4687ad) and both ConfigMaps showcounter: "1". -
Commit
counter: "2"torepro/sre-2567/values.yaml(8e4836e2) and push. -
Result after 6 minutes and three reconciliations (
timeout.reconciliation: 120s):Application sync.status desired revisions last synced revisions ConfigMap countersre-2567-path-and-refOutOfSync 8e4836e2,8e4836e2cc4687ad,cc4687ad1sre-2567-split-refSynced 8e4836e2(x3)8e4836e2(x3)2For
sre-2567-path-and-ref,spec.sourcesequalsstatus.operationState.syncResult.sources, and the last operation was the initial automated sync.
Because both sources point to the same repository and branch, the revision of the path source also changed in this test. The app is skipped anyway, so the bug is not limited to value-file changes.
Expected behavior
A source with path is rendered, so it should be evaluated for changes like any non-ref source, whether or not it also sets ref. sre-2567-path-and-ref should auto-sync 8e4836e2 the same way sre-2567-split-ref does, as it did on 3.3.x.
One possible fix is to apply the ref-source short-circuit only to pure value-file sources, for example source.IsRef() && source.Path == "".
Version
$ argocd version
argocd: v3.5.4+d6d5b24.dirty
BuildDate: 2026-10-06T23:03:39Z
GitCommit: d6d5b248ce00e1a2c512068002a93d3319767087
GitTreeState: dirty
GitTag: v3.5.4
GoVersion: go1.27.1
Compiler: gc
Platform: darwin/arm64
argocd-server: v3.4.12Logs
Application controller, same reconcile window:
time="2026-10-28T16:44:03Z" level=info msg="Skipping auto-sync: most recent sync already to [8e4836e212c3e190d69317fb3db2db6fad5acff7 8e4836e212c3e190d69317fb3db2db6fad5acff7]" app-namespace=argocd application=sre-2567-path-and-ref project=default
time="2026-10-28T16:44:04Z" level=info msg="Initiated automated sync to '8e4836e212c3e190d69317fb3db2db6fad5acff7, 8e4836e212c3e190d69317fb3db2db6fad5acff7, 8e4836e212c3e190d69317fb3db2db6fad5acff7'" app-namespace=argocd application=sre-2567-split-ref project=default
time="2026-10-28T16:46:34Z" level=info msg="Skipping auto-sync: most recent sync already to [8e4836e212c3e190d69317fb3db2db6fad5acff7 8e4836e212c3e190d69317fb3db2db6fad5acff7]" app-namespace=argocd application=sre-2567-path-and-ref project=default
time="2026-10-28T16:48:49Z" level=info msg="Skipping auto-sync: most recent sync already to [8e4836e212c3e190d69317fb3db2db6fad5acff7 8e4836e212c3e190d69317fb3db2db6fad5acff7]" app-namespace=argocd application=sre-2567-path-and-ref project=default
The message prints the desired revisions. The last sync was to cc4687ad, so the wording "most recent sync already to" is misleading in this case.
Workaround
Do not set ref on a source that has path. Add a separate ref-only source with the same repoURL and targetRevision, as in sre-2567-split-ref.
Activity
Cindia-blue commentedon Oct 28, 2026
I'd like to work on this regression. I will first reproduce it with a focused controller regression test, then check that sources which both render manifests and declare a ref are evaluated for revision changes, while keeping pure values-only sources and manifest-generate-paths behavior intact.
I found no linked implementation or existing claim in the issue timeline. Please let me know if anyone is already working on this, or if maintainers prefer a different scope. I will validate the local change and wait for maintainer scope confirmation before opening a PR.