UXP/Crossplane version upgrade fails in Spaces with "not a supported crossplane version" webhook error
Last updated: September 30, 2026
Issue
When attempting to upgrade the UXP/Crossplane version on a Spaces control plane (e.g. from 1.20.10-up.3 to 2.0.8-up.4), the upgrade fails. ArgoCD reports an error similar to:
error when patching "/dev/shm/<id>": admission webhook "controlplanes.spaces.upbound.io" denied the request: spec.crossplane.version "2.0.8-up.4" is not a supported crossplane version
The crossplane-versions-public ConfigMap does not list the target version, even though the image appears to be present in the registry.
Environment
Spaces v1.17.1 (Self-Hosted)
UXP/Crossplane upgrade targeting v2.x (e.g.
2.0.8-up.4)Air-gapped or mirrored registry setup
ArgoCD used for GitOps deployment
EKS (or similar Kubernetes distribution)
Commonly triggered when upgrading from Crossplane 1.x to 2.x in an environment with a private/mirrored registry.
Resolution
Step 1: Prevent auto-upgrades from overriding Git as the source of truth
Set the auto-upgrade channel to None on your ControlPlane to ensure Git controls the version:
spec:
crossplane:
autoUpgrade:
channel: None
version: <TARGET_VERSION>
Step 2: Prevent ArgoCD from overwriting the versions ConfigMap
Add the following ignoreDifferences rule to your Spaces ArgoCD Application to stop Argo from reverting the ConfigMap to the chart's default list:
spec:
ignoreDifferences:
- group: ""
kind: ConfigMap
name: crossplane-versions-public
namespace: upbound-system
jsonPointers:
- /data/versions
- /data/latest
syncPolicy:
syncOptions:
- RespectIgnoreDifferences=true
Step 3: Verify what is present in your registry
Run the following commands, replacing <YOUR_REGISTRY> with your registry path (e.g. my-registry.example.com/spaces-artifacts):
REG=<YOUR_REGISTRY>
crane manifest $REG/crossplane:v<TARGET_VERSION>
crane manifest $REG/controller-manager:v<TARGET_VERSION>
crane manifest $REG/crossplane:<TARGET_VERSION>
The first two are the images (prefixed with v). The third is the chart tag (without v). Note which of these return 404 or MANIFEST_UNKNOWN.
Step 4: Mirror any missing artifacts - images first, chart last
crane copy xpkg.upbound.io/spaces-artifacts/crossplane:v<TARGET_VERSION> $REG/crossplane:v<TARGET_VERSION>
crane copy xpkg.upbound.io/spaces-artifacts/controller-manager:v<TARGET_VERSION> $REG/controller-manager:v<TARGET_VERSION>
crane copy xpkg.upbound.io/spaces-artifacts/crossplane:<TARGET_VERSION> $REG/crossplane:<TARGET_VERSION>
Always mirror the images (v-prefixed tags) before the chart tag (without v).
Step 4: Force the Spaces versions controller to re-read the registry
kubectl -n upbound-system annotate cm crossplane-versions-public \
spaces.upbound.io/refresh="$(date +%s)" --overwrite
kubectl -n upbound-system get cm crossplane-versions-public \
-o jsonpath='{.data.versions}'
Confirm that the target version (without the v prefix, e.g. 2.0.8-up.4) now appears in the output before proceeding.
Step 6: Re-sync the ControlPlane in ArgoCD
Use spec.crossplane.version: <TARGET_VERSION> (without the v prefix) in your ControlPlane manifest.
Step 7: Upgrade sequentially across minor versions
When upgrading across multiple minor versions (e.g. 2.0 → 2.1 → 2.2), upgrade one minor version at a time. Do not skip minor versions.
Cause
The Spaces admission webhook only accepts Crossplane versions that are present in the crossplane-versions-public ConfigMap. This ConfigMap is populated by the Spaces versions controller by scanning chart tags (without the v prefix) in the configured registry.
In Crossplane 1.x, Helm charts and container images were stored in separate repositories. In Crossplane 2.x, both the chart and images reside in the same crossplane repository but under different tags:
Images use a
v-prefixed tag (e.g.crossplane:v2.0.8-up.4)The chart uses a tag without
v(e.g.crossplane:2.0.8-up.4)
Mirroring pipelines that only track image tags will copy the v-prefixed tags but miss the chart tag. Because the versions controller looks for the chart tag, the version never appears in the ConfigMap, and the webhook rejects the upgrade request.
Additionally, the ControlPlane CRD also defaults autoUpgrade.channel to Stable, so any manifest that only sets crossplane.version is affected. Spaces may auto-upgrade the control plane to the latest stable version, ArgoCD then tries to revert it to the specified version, and the webhook rejects that change. Set autoUpgrade.channel: None to avoid this.
2.x control planes also need a new image, controller-manager:v2.0.8-up.4, from its own controller-manager repository.