You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue was written and debugged with claude-code
What happened?
When the --observed-resources file contains the XR (alongside the composed resources), all of the observed composed resources are silently dropped, the function pipeline runs with an empty observed state and the output is wrong, with no error.
This is easy to hit because crossplane render always emits the XR as the first document, so the common "render once, feed the output back as observed for a second render" workflow includes the XR by default. The same inputs worked on the v2.2 CLI.
Cause: the observed file contains a copy of the XR, which gets loaded into the render engine's in-memory store keyed by GVK + namespace + name, after the real XR — so the UID-less observed copy overwrites the engine's XR. With the XR's UID now empty, the reconciler keeps only composed resources whose controller ownerReference.UID matches the XR's UID, and drops all of them. v2.2 didn't run observed resources through this ownership check, so the extra XR document was harmless there.
How can we reproduce it?
Any composition with a function pipeline that composes at least one resource:
The second render's functions see no observed resources. Removing the XR document from observed.yaml (e.g. yq 'select(.kind != "<XR-kind>")') makes it work again.
Suggested fix
The XR is already supplied as the positional argument, so a copy of it in --observed-resources is redundant. Skipping it in cmd/crossplane/render/convert.go (BuildCompositeRequest, before converting observed resources) fixes it without any engine change:
Note
This issue was written and debugged with claude-code
What happened?
When the
--observed-resourcesfile contains the XR (alongside the composed resources), all of the observed composed resources are silently dropped, the function pipeline runs with an empty observed state and the output is wrong, with no error.This is easy to hit because
crossplane renderalways emits the XR as the first document, so the common "render once, feed the output back as observed for a second render" workflow includes the XR by default. The same inputs worked on the v2.2 CLI.Cause: the observed file contains a copy of the XR, which gets loaded into the render engine's in-memory store keyed by GVK + namespace + name, after the real XR — so the UID-less observed copy overwrites the engine's XR. With the XR's UID now empty, the reconciler keeps only composed resources whose controller
ownerReference.UIDmatches the XR's UID, and drops all of them. v2.2 didn't run observed resources through this ownership check, so the extra XR document was harmless there.How can we reproduce it?
Any composition with a function pipeline that composes at least one resource:
crossplane render xr.yaml composition.yaml functions.yaml > observed.yaml crossplane render xr.yaml composition.yaml functions.yaml --observed-resources observed.yamlThe second render's functions see no observed resources. Removing the XR document from
observed.yaml(e.g.yq 'select(.kind != "<XR-kind>")') makes it work again.Suggested fix
The XR is already supplied as the positional argument, so a copy of it in
--observed-resourcesis redundant. Skipping it incmd/crossplane/render/convert.go(BuildCompositeRequest, before converting observed resources) fixes it without any engine change:Happy to send a PR.
What environment did it happen in?