IaC Error Guide: 'cannot resolve' Crossplane Composite Not Ready
Fix Crossplane composite resources stuck not Ready: diagnose unhealthy providers, bad ProviderConfig credentials, composition selectors, and patch errors.
- #iac
- #troubleshooting
- #errors
- #crossplane
Overview
This error happens when a Crossplane claim or composite resource (XR) never reaches READY=True because one of the managed resources it composes cannot be created or reconciled. The composite reports SYNCED=True (the composition rendered) but READY=False, and the provider that owns the underlying managed resource cannot resolve it against the cloud API. The whole claim hangs in a pending state.
You will see this in the claim or composite status conditions:
$ kubectl describe xpostgresqlinstance my-db-abc12
Status:
Conditions:
Type Reason Status Message
Synced ReconcileSuccess True
Ready Creating False
Events:
Warning CannotResolveResourceReferences cannot resolve references:
referenced field was empty (referenced resource may not yet be ready)
It occurs because Crossplane reconciliation is layered: the claim binds to a composite, the composite renders managed resources via a Composition, and each managed resource is reconciled by a provider against a real cloud API. A failure at any layer — an unhealthy provider, bad credentials, a selector that matches nothing, or a cloud API rejection — surfaces as a composite that is synced but never ready.
Symptoms
- A claim stays
READY=Falseindefinitely whileSYNCED=True. kubectl get managedshows a resource withSYNCED=TruebutREADY=False.- Events show
CannotResolveResourceReferencesorcannot resolve. kubectl get providersshows a provider that is notHEALTHY.
kubectl get xpostgresqlinstance
NAME SYNCED READY COMPOSITION AGE
my-db-abc12 True False xpostgresqlinstances... 6m
kubectl get managed
NAME READY SYNCED EXTERNAL-NAME AGE
rdsinstance.database.aws.../my-db-abc12-x False True 6m
Common Root Causes
1. The provider is not installed or not Healthy
If the provider package is not HEALTHY, it cannot reconcile any managed resource of its kind.
kubectl get providers
NAME INSTALLED HEALTHY PACKAGE AGE
provider-aws-rds True False xpkg.upbound.io/.../provider-aws-rds... 9m
HEALTHY=False means the provider pod is crashlooping or its CRDs failed to install — nothing of that kind will reconcile.
2. ProviderConfig credentials are wrong or missing
The managed resource references a ProviderConfig, but its credentials secret is absent or invalid, so every cloud call is rejected.
kubectl get providerconfig
kubectl describe rdsinstance my-db-abc12-x | grep -A3 Events
NAME AGE
default 9m
Events:
Warning CannotConnectToProvider cannot get terminal client:
InvalidClientTokenId: The security token included in the request is invalid
The credentials in the ProviderConfig secret are invalid, so the provider cannot authenticate to AWS.
3. Composition selector matches nothing
The composite’s compositionRef/compositionSelector does not match an installed Composition, so no managed resources are rendered.
kubectl get composition
kubectl describe xpostgresqlinstance my-db-abc12 | grep -i composition
NAME XR-KIND XR-APIVERSION
xpostgresqlinstances.gcp.example XPostgreSQLInstance database.example.org/v1alpha1
Composition Ref: <none>
The only Composition present targets GCP, but the claim selects an AWS one that does not exist — so nothing is composed.
4. Managed resource stuck SYNCED=True READY=False on a cloud API error
The composition rendered correctly and the provider is healthy, but the cloud API rejected the create (quota, invalid parameter, name conflict).
kubectl describe rdsinstance my-db-abc12-x | grep -A5 'Conditions\|Events'
Type Reason Status Message
Synced ReconcileSuccess True
Ready Unavailable False
Events:
Warning CannotCreateExternalResource create failed:
InvalidParameterValue: The parameter MasterUserPassword is not a valid password
because it is shorter than 8 characters.
SYNCED=True (Crossplane did its part) but READY=False because AWS rejected the parameter.
5. Missing CRDs (XRD not established)
The CompositeResourceDefinition (XRD) is not established, so the claim’s CRD does not exist and the composite cannot be created.
kubectl get xrd
kubectl get crd | grep postgresql
NAME ESTABLISHED OFFERED AGE
xpostgresqlinstances.database.example.org False True 8m
ESTABLISHED=False means the XRD failed to register its CRDs — claims of that kind will not reconcile.
6. Patch / transform error in the Composition
A patch references a field path that does not exist or a transform fails, so the rendered managed resource is malformed.
kubectl get events --field-selector reason=ComposeResources --sort-by=.lastTimestamp
LAST SEEN TYPE REASON OBJECT MESSAGE
30s Warning ComposeResources xpostgresqlinstance/my-db-... cannot compose resources:
cannot render composed resource from resource template at index 0:
cannot resolve patch: cannot get value at field path spec.parameters.storageGb:
no such field
The patch reads spec.parameters.storageGb, but the claim sets storageGB — the field path mismatch breaks composition.
Diagnostic Workflow
Step 1: Read the composite/claim status conditions
kubectl describe xpostgresqlinstance <NAME>
Note whether Synced and Ready are true/false and read the Message. Synced=False points at composition/XRD problems; Synced=True, Ready=False points at the managed resource layer.
Step 2: Confirm providers and ProviderConfigs are healthy
kubectl get providers
kubectl get providerconfig
Any provider that is not HEALTHY, or a missing ProviderConfig, blocks every managed resource it owns (causes 1 and 2).
Step 3: Drill into the managed resource
kubectl get managed
kubectl describe <managed-resource>.<group> <NAME>
Read the Conditions and Events. A cloud API error here (cause 4) names the exact rejected parameter or quota.
Step 4: Verify the XRD and Composition wiring
kubectl get xrd
kubectl get composition
kubectl get crd | grep <kind>
ESTABLISHED=False on the XRD (cause 5) or a selector that matches no Composition (cause 3) explains a composite that never renders managed resources.
Step 5: Inspect events for compose/patch failures
kubectl get events --sort-by=.lastTimestamp | tail -20
ComposeResources warnings with no such field or cannot resolve patch reveal patch/transform errors (cause 6) that you fix in the Composition or claim spec.
Example Root Cause Analysis
A developer creates a PostgreSQLInstance claim. It reports SYNCED=True but never becomes ready, and the underlying RDS instance shows READY=False.
The claim status itself is unremarkable, so drill into the managed resource:
kubectl describe rdsinstance my-db-abc12-x | grep -A4 Events
Events:
Warning CannotCreateExternalResource create failed:
InvalidParameterValue: The parameter MasterUserPassword is not a valid password
because it is shorter than 8 characters.
The provider is healthy and the composition rendered fine — SYNCED=True. The failure is purely at the cloud API: AWS rejected the short master password that the Composition derived from the claim’s passwordSecretRef. Checking the referenced secret confirms it holds a 6-character value.
The fix is to supply a valid password in the referenced secret and let Crossplane re-reconcile:
kubectl create secret generic db-conn \
--from-literal=password='Sup3rSecret99' \
--dry-run=client -o yaml | kubectl apply -f -
secret/db-conn configured
Crossplane re-reconciles the rdsinstance on its next loop. With a valid password the AWS create succeeds, the managed resource flips to READY=True, and the composite — and therefore the claim — becomes ready. The durable fix is to validate secret contents (length/format) before they feed a Composition, so a bad input fails fast instead of stalling reconciliation.
Prevention Best Practices
- Gate rollouts on provider health: alert when
kubectl get providersshows any provider notHEALTHY, since that silently blocks every managed resource it owns. Codify provider and ProviderConfig manifests alongside your infrastructure-as-code guides. - Validate ProviderConfig credentials immediately after install with a throwaway managed resource, so invalid tokens surface before real claims depend on them.
- Keep field paths in Composition patches in lock-step with the XRD’s
openAPIV3Schema; a renamed parameter is the most commoncannot resolve patchcause. - Wait for XRDs to report
ESTABLISHED=Truebefore applying claims in automation, and order XRD/Composition installs ahead of claims in your GitOps sync waves. - Surface managed-resource conditions in dashboards, not just claim readiness, so a cloud API rejection deep in the stack is visible at a glance.
- When a claim hangs and you need a fast read of the layered status, the free incident assistant can summarize the provider/composition/managed-resource conditions into the likely cause.
Quick Command Reference
# Composite / claim readiness and conditions
kubectl get xpostgresqlinstance
kubectl describe xpostgresqlinstance <NAME>
# Provider and credential health
kubectl get providers
kubectl get providerconfig
# Managed resources and their cloud-API errors
kubectl get managed
kubectl describe <managed-resource>.<group> <NAME>
# Composition / XRD wiring
kubectl get xrd
kubectl get composition
kubectl get crd | grep <kind>
# Compose / patch failures
kubectl get events --sort-by=.lastTimestamp | tail -20
Conclusion
A Crossplane composite that is synced but never ready means one layer of the claim -> composite -> managed-resource -> cloud chain failed to resolve. The usual root causes:
- The provider is not installed or not
HEALTHY. - The
ProviderConfigcredentials are missing or invalid. - The composition selector matches no installed Composition.
- A managed resource is
SYNCED=True, READY=Falsebecause the cloud API rejected it. - The XRD is not
ESTABLISHED, so the claim’s CRD never registered. - A patch/transform in the Composition references a field path that does not exist.
Read the conditions top-down — claim, composite, managed resource — and let the layer that is READY=False point you at the provider, credentials, composition, or cloud-API fix.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.