Repository navigation
ssaForceConflicts fails on releases installed before Helm v4: "forceConflicts enabled when serverSideApply disabled" #403
Description
What happened?
After upgrading provider-helm from v1.0.6 (Helm v3 SDK) to v1.4.x (Helm v4 SDK), any existing Release that has spec.forProvider.ssaForceConflicts: true fails to upgrade:
Upgrade "<release>" failed: invalid client update option(s): forceConflicts enabled when serverSideApply disabled
The Helm release goes to failed and the Release stays Ready=False.
Root cause: pkg/clients/helm/client.go sets ForceConflicts from ssaForceConflicts on the upgrade and rollback actions, but it never sets ServerSideApply. That leaves Helm's default of "auto", which uses server-side apply only when the previous revision's ApplyMethod == "ssa" (helm v4.2.3, pkg/action/upgrade.go getUpgradeServerSideValue). Revisions written by Helm v3 (so by provider-helm <= v1.0.x) have no ApplyMethod, so Helm resolves to client-side apply. Helm then rejects the combination in kube.ClientUpdateOptionServerSideApply (pkg/kube/client.go: if !serverSideApply && forceConflicts { return errors.New(...) }).
As a result, ssaForceConflicts can never be used on releases created before the Helm v4 migration. Fresh installs work, because Helm v4 installs with SSA by default.
This was anticipated in #333: "old release that used client-side apply will continue to do so. We could override this behavior to always prefer SSA with forced conflict resolution by setting ServerSideApply and ForceConflicts on helm actions to true … Maybe it could be introduced behind a spec field e.g. ssaForceConflicts". The field was added in 4e1f8de, but only ForceConflicts is wired.
Expected: with ssaForceConflicts: true, the upgrade (and rollback) switches the release to server-side apply and force-resolves conflicts, including the ones left over from client-side apply field managers.
How can we reproduce it?
- Install provider-helm v1.0.6 and create a
Release, for example:Wait until it isapiVersion: helm.crossplane.io/v1beta1 kind: Release metadata: name: example spec: forProvider: chart: name: <any-chart> repository: <repo> version: <v1> namespace: default providerConfigRef: name: helm
Ready. The Helm release secret for revision 1 has noapply_method. - Upgrade provider-helm to v1.4.x.
- Update the
Release: bumpchart.versionand setssaForceConflicts: true. - The upgrade fails with
forceConflicts enabled when serverSideApply disabled.
Proposed fix
When SSAForceConflicts is set, also force SSA on the upgrade and rollback actions:
--- a/pkg/clients/helm/client.go
+++ b/pkg/clients/helm/client.go
@@ uc := action.NewUpgrade(actionConfig)
uc.ForceConflicts = args.SSAForceConflicts
+ if args.SSAForceConflicts {
+ // Helm's default "auto" keeps client-side apply for releases that were
+ // installed with it (e.g. by Helm v3), which Helm rejects in combination
+ // with ForceConflicts. Force server-side apply so such releases migrate.
+ uc.ServerSideApply = "true"
+ }
@@ rb := action.NewRollback(actionConfig)
rb.ForceConflicts = args.SSAForceConflicts
+ if args.SSAForceConflicts {
+ rb.ServerSideApply = "true"
+ }This builds, and the pkg/clients/helm unit tests still pass. Happy to open a PR.
Workaround until fixed: patch the latest Helm release secret so its apply_method is "ssa". Helm's "auto" then picks server-side apply.
What environment did it happen in?
- Crossplane version: v2.3.5
- provider-helm:
xpkg.upbound.io/upbound/provider-helm:v1.4.1(upgraded from v1.0.6); code is unchanged onmainas of 37ce1ab - Helm SDK: helm.sh/helm/v4 v4.2.3
- Kubernetes: v1.36.2+k3s1
Activity
jonasz-lasut commentedon Sep 23, 2026
Hi @jerryguowei thank you for opening the issue. We're open to contributions and this seems like a proper resolution of this issue. I'll have a look when the PR is opened
fix(release): enable server-side apply with ssaForceConflicts