Flux source-controller Fails to Fetch Helm Repository Index
Flux source-controller Helm index fetch failure occurs when the source-controller pod cannot retrieve index.yaml from a Helm repository, so the HelmRepository resource never reaches a ready state. Every HelmRelease that depends on that repository stops receiving chart updates. The cluster keeps running the version it already has, so nothing appears broken while deployments quietly stop being delivered.
source-controller reconciles HelmRepository resources by fetching index.yaml on an interval. When that fetch fails, the repository is marked not-ready and every dependent HelmRelease stalls. Running workloads are unaffected, which is exactly the problem: GitOps reports success on the Git side while chart delivery has stopped. The error text tells you which layer failed, and the progression between error types tells you whether the registry is down or the network path is.
What does the Flux source-controller Helm index error look like?
source-controller logs structured JSON. The useful field is error, and it nests three
levels deep: the reconcile failed because the index fetch failed because the underlying
HTTP request failed.
{"level":"error","msg":"Reconciler error","controller":"helmrepository",
"controllerKind":"HelmRepository",
"HelmRepository":{"name":"charts-internal","namespace":"flux"},
"error":"failed to fetch Helm repository index: failed to cache index to
temporary file: Get \"https://charts.internal.example:443/artifactory/
helm-virtual-all/index.yaml\": dial tcp 10.20.30.40:443: connect: connection refused"}
The same reconcile loop produces different tail errors as conditions change. A real twelve-minute window progressed through all three:
| Error tail | What it means |
|---|---|
connect: connection refused |
Nothing is listening. Registry down, or the service or endpoint is gone. |
dial tcp ...: i/o timeout |
Packets leaving, nothing coming back. Network policy, firewall, or an overloaded host. |
503 |
The registry answered. It is up but unhealthy or rate limiting. |
That progression is diagnostic on its own. Refused to timeout to 503 is the signature of a registry restarting and coming back under load, not a misconfiguration on the cluster side. A configuration error produces the same error text every time.
HelmRepository is ready but the chart itself will not resolve, the index was retrieved successfully and the problem is a missing chart version or a semver constraint that matches nothing. Check which resource is not-ready before assuming a network cause.
Why does source-controller fail to fetch the index?
source-controller runs one reconcile per HelmRepository per spec.interval. Each
reconcile fetches index.yaml, caches it to a temp file, and writes an artifact other
controllers consume. Failure at any step marks the resource not-ready and schedules a retry.
The realistic causes, roughly in order of frequency:
- The registry is genuinely down or restarting. Self-hosted Artifactory, Nexus, and ChartMuseum instances get patched, and a rolling restart produces exactly this window.
- Network path broken between cluster and registry. A NetworkPolicy change, a firewall rule, or an egress gateway failure. Common when the registry lives outside the cluster on a private network.
- Credentials expired.
spec.secretRefpoints at a secret with a rotated token. This produces401or403rather than a dial error. - TLS trust failure. A private CA whose bundle is not mounted, or a certificate that
expired. Produces
x509errors in the tail. - DNS resolution failure. Registry hostname no longer resolves from inside the cluster, often after a CoreDNS or upstream resolver change.
The tail of the error string identifies which one you have. Read to the end of the line.
What breaks while the index fetch is failing?
Nothing that a dashboard reports.
Pods keep running. Services keep serving. Git commits keep landing and kustomize-controller
keeps reporting success on everything that is not chart-based. The failure is confined to
delivery of new chart versions:
HelmReleaseupgrades stall. A merged change to a chart version is never applied.- New
HelmReleaseresources never install, because their source is not ready. - Drift correction stops for chart-managed workloads. Manual changes made to those resources are no longer reverted.
- The deploy pipeline reports success. CI merged, Flux synced Git, and the chart layer failed downstream of both.
The gap between “deployed” and “actually running” opens silently and stays open until someone notices a version mismatch, often during an unrelated investigation.
How do I fix a Flux Helm repository index fetch failure?
1. Confirm which resource is failing
flux get sources helm --all-namespaces
kubectl -n flux describe helmrepository charts-internal
The Ready condition carries the same error text as the controller log, and the
Last Transition Time tells you how long delivery has been stopped.
2. Read the tail of the error
Match the tail to a cause before touching anything:
connection refused → registry process down or endpoint missing
i/o timeout → network path blocked
503 / 502 → registry up but unhealthy
401 / 403 → credentials rejected
x509 → TLS trust or expiry
no such host → DNS
3. Test the fetch from inside the cluster
Namespace and network policy both matter, so test from a pod in the same namespace as
source-controller, not from your laptop:
kubectl -n flux run netcheck --rm -it --image=curlimages/curl --restart=Never -- \
curl -sS -o /dev/null -w '%{http_code}\n' \
https://charts.internal.example/artifactory/helm-virtual-all/index.yaml
A different result from inside the cluster than from outside points at NetworkPolicy or egress rather than the registry.
4. Check credentials and TLS if the fetch is reaching the registry
kubectl -n flux get helmrepository charts-internal -o jsonpath='{.spec.secretRef.name}'
kubectl -n flux get secret <name> -o jsonpath='{.data}' | head -c 200
For a private CA, confirm spec.certSecretRef is set and the CA bundle in that secret has
not expired.
5. Force a reconcile once the cause is cleared
flux reconcile source helm charts-internal -n flux
flux get sources helm -n flux
Without this the resource waits out the remainder of spec.interval, which can be fifteen
minutes or more, and it will look like the fix did not work.
6. Make the next one visible
The reason this class of failure runs long is that no alert exists for a not-ready source.
If you are alerting, alert on HelmRepository ready-condition age rather than on pod
health, because the pod is fine throughout. If you would rather not add another rule to
maintain, this is precisely the shape of failure a baseline catches on its own.
How does Dstl8 detect this?
source-controller is one service among hundreds in a cluster, and a reconcile error is not a crash, a restart, or a latency spike. No threshold catches it. Dstl8 baselines each service against its own history, so a burst of reconcile errors from a controller that normally emits none surfaces as an incident, correlated across the namespace and service that produced it.
The incident correlates the namespace and the service, cites the specific error progression rather than a generic failure count, and closes on its own when reconciles succeed again. The error-type progression is preserved in the narrative, which is the part that tells you the registry was restarting rather than misconfigured.
Frequently asked questions
Why does Flux source-controller fail to fetch a Helm repository index?
The source-controller pod could not retrieve index.yaml from the Helm repository. Common causes are the registry being down or restarting, a blocked network path from a NetworkPolicy or firewall, expired credentials in the referenced secret, a TLS trust or certificate expiry problem, or DNS resolution failure. The tail of the error string identifies which one applies.
What happens to HelmReleases when a HelmRepository is not ready?
Every HelmRelease depending on that repository stops receiving chart updates. Running workloads continue unaffected, new releases never install, and drift correction stops for chart-managed resources. The deploy pipeline still reports success because Git synced correctly, so the failure is silent.
How do I force Flux to retry a failed Helm repository fetch?
Run flux reconcile source helm <name> -n <namespace>. Without forcing it, the controller waits out the remainder of spec.interval, which can be fifteen minutes or more, and it will appear that the fix did not work.
Related patterns
References
- Flux docs: HelmRepository API reference
- Flux docs: source-controller
- Flux docs: Troubleshooting
- Helm docs: The chart repository index file
Catch stalled delivery before the version drift does.
Dstl8 baselines every controller and service in your cluster and surfaces error patterns that were not there before. No thresholds, no rules, no prior knowledge of what to look for.














