Repository: kubernetes-sigs/kro

Issue:
rgd-controller: Observed a panic - with nested RGD
### Description

<!-- Please craft a detailed description of the documentation issue or suggestion -->
<!-- Below are some suggestions but feel free to modify or remove them -->
Hello,
I'm trying out Kro and as a first step for more advanced setup I attempted to create some basic RGD and extend it with another RGD. This failed with Panic error in Kro controller. This seems similar to #852 however, I don't use if statement to conditionally include a resource, and panic seems to be in reconcile controller.

**Observed Behavior**:
Panic error in RGD controller and CRD not getting applied/deployed. ResourceGraphDefinition is created, however doesn't get to Active state

**Expected Behavior**:
ResourceGrapghDefinition being successfully deployed and CRD created

**Reproduction Steps** (Please include `ResourceGraphDefinition` and `Instances` files):
1. Deploy BasicApp RGD:
```yaml
---
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: basic-app
spec:
  schema:
    apiVersion: v1alpha1
    kind: BasicApp
    spec:
      name: string
      image: string | default="nginx"
      replicas: integer | default=3
    status:
      deploymentConditions: ${deployment.status.conditions}
      availableReplicas: ${deployment.status.availableReplicas}
  resources:
    - id: deployment
      template:
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: ${schema.spec.name}
        spec:
          replicas: ${schema.spec.replicas}
          selector:
            matchLabels:
              app: ${schema.spec.name}
          template:
            metadata:
              labels:
                app: ${schema.spec.name}
            spec:
              containers:
                - name: ${schema.spec.name}
                  image: ${schema.spec.image}
                  ports:
                    - containerPort: 80
```

2. Deploy AdvancedApp RGD:**Versions**:
```yaml
---
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: advanced-app
spec:
  schema:
    apiVersion: v1alpha1
    kind: AdvancedApp
    spec:
      name: string
      image: string | default="nginx"
      replicas: integer | default=3
  resources:
    - id: rgddep
      template:
        apiVersion: kro.run/v1alpha1
        kind: BasicApp
        metadata:
          name: my-advanced-app-${schema.spec.name}
        spec:
          replicas: ${schema.spec.replicas}
          name: ${schema.spec.name}
          image: ${schema.spec.image}
```

**Kro and Kubernetes versions**:
- kro version: 0.6.3
- Kubernetes Version (`kubectl version`): 
  - 1.33.5 EKS
  - 1.32.9 AKS

**Error Logs** (if applicable)**:
```
2025-11-25T11:54:21.155Z        DEBUG   rgd-controller  setting resourcegraphdefinition as managed      {"controller": "ResourceGraphDefinition", "controllerGroup": "kro.run", "controllerKind": "ResourceGraphDefinition", "name": "advanced-app", "reconcileID": "1f27285b-be4d-4eeb-a8cd-7809b551b680"}
2025-11-25T11:54:21.155Z        DEBUG   rgd-controller  reconciling resource graph definition graph     {"controller": "ResourceGraphDefinition", "controllerGroup": "kro.run", "controllerKind": "ResourceGraphDefinition", "name": "advanced-app", "reconci
2025-11-25T11:54:21.172Z        ERROR   rgd-controller  Observed a panic        {"controller": "ResourceGraphDefinition", "controllerGroup": "kro.run", "controllerKind": "ResourceGraphDefinition", "name": "advanced-app", "reconcileID": "1f27285b-be4d-4eeb-a8cd-7809b551b680", "panic": "runtime error: invalid memory address or nil pointer dereference", "panicGoValue": "\"invalid memory address or nil pointer dereference\"", "stacktrace": "goroutine 131 [running]:\nk8s.io/apimachinery/pkg/util/runtime.logPanic({0x1ec7738, 0xc0013ee2a0}, {0x18d6020, 0x2ba6960})\n\tk8s.io/apimachinery@v0.34.1/pkg/util/runtime/runtime.go:132 +0xbc\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile.func1()\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:105 +0x112\npanic({0x18d6020?, 0x2ba6960?})\n\truntime/panic.go:783 +0x132\nk8s.io/apiserver/pkg/cel/openapi.isExtension(...)\n\tk8s.io/apiserver@v0.34.1/pkg/cel/openapi/extensions.go:28\nk8s.io/apiserver/pkg/cel/openapi.isXEmbeddedResource(...)\n\tk8s.io/apiserver@v0.34.1/pkg/cel/openapi/extensions.go:39\nk8s.io/apiserver/pkg/cel/openapi.(*Schema).IsXEmbeddedResource(0xc00129c588?)\n\tk8s.io/apiserver@v0.34.1/pkg/cel/openapi/adaptor.go:192 +0x11\ngithub.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata({0x1ee5f08, 0xc00129c550}, 0x0)\n\tgithub.com/kubernetes-sigs/kro/pkg/cel/schemas.go:107 +0x297\ngithub.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata({0x1ee5f08, 0xc00129c4b0}, 0x0)\n\tgithub.com/kubernetes-sigs/kro/pkg/cel/schemas.go:92 +0xa05\ngithub.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata({0x1ee5f08, 0xc00129c440}, 0x0)\n\tgithub.com/kubernetes-sigs/kro/pkg/cel/schemas.go:143 +0xd46\ngithub.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata({0x1ee5f08, 0xc00129c400}, 0x0)\n\tgithub.com/kubernetes-sigs/kro/pkg/cel/schemas.go:143 +0xd46\ngithub.com/kubernetes-sigs/kro/pkg/cel.DefaultEnvironment({0xc000d666a8, 0x1, 0x6?})\n\tgithub.com/kubernetes-sigs/kro/pkg/cel/environment.go:106 +0x5d3\ngithub.com/kubernetes-sigs/kro/pkg/cel.TypedEnvironment(0x19214a0?)\n\tgithub.com/kubernetes-sigs/kro/pkg/cel/environment.go:148 +0x3b\ngithub.com/kubernetes-sigs/kro/pkg/graph.buildStatusSchema(0xc00042c460, 0xc0013ee540)\n\tgithub.com/kubernetes-sigs/kro/pkg/graph/builder.go:610 +0x269\ngithub.com/kubernetes-sigs/kro/pkg/graph.(*Builder).buildInstanceResource(0x1920ea0?, {0xc0008889f8, 0x7}, {0xc0008889f0, 0x8}, {0xc000888a00, 0xb}, 0xc00042c460, 0xc0013ee540)\n\tgithub.com/kubernetes-sigs/kro/pkg/graph/builder.go:486 +0x158\ngithub.com/kubernetes-sigs/kro/pkg/graph.(*Builder).NewResourceGraphDefinition(0xc00047f560, 0x0?)\n\tgithub.com/kubernetes-sigs/kro/pkg/graph/builder.go:183 +0x346\ngithub.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition.(*ResourceGraphDefinitionReconciler).reconcileResourceGraphDefinitionGraph(0x1eccc38?, {0xc0013ee360?, 0xc00047c6f0?}, 0x1c31aba?)\n\tgithub.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition/controller_reconcile.go:132 +0x29\ngithub.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition.(*ResourceGraphDefinitionReconciler).reconcileResourceGraphDefinition(0xc00059c680, {0x1ec7738, 0xc0013ee2a0}, 0xc00084a180)\n\tgithub.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition/controller_reconcile.go:47 +0x1f7\ngithub.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition.(*ResourceGraphDefinitionReconciler).Reconcile(0xc00059c680, {0x1ec7738, 0xc0013ee2a0}, 0xc00084a180)\n\tgithub.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition/controller.go:172 +0xdb\nsigs.k8s.io/controller-runtime/pkg/reconcile.(*objectReconcilerAdapter[...]).Reconcile(0x1ea7d40, {0x1ec7738, 0xc0013ee2a0}, {{{0x0, 0x0?}, {0xc0008882c0?, 0x1bf1750?}}})\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile/reconcile.go:154 +0x16f\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile(0xc0013ee1e0?, {0x1ec7738?, 0xc0013ee2a0?}, {{{0x0?, 0x0?}, {0xc0008882c0?, 0x0?}}})\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:116 +0xbf\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler(0x1ed8e80, {0x1ec7770, 0xc0005b8410}, {{{0x0, 0x0}, {0xc0008882c0, 0xc}}})\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:303 +0x3a5\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem(0x1ed8e80, {0x1ec7770, 0xc0005b8410})\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:263 +0x20d\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.2()\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:224 +0x85\ncreated by sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2 in goroutine 75\n\tsigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:220 +0x48d\n"}
k8s.io/apimachinery/pkg/util/runtime.logPanic
        k8s.io/apimachinery@v0.34.1/pkg/util/runtime/runtime.go:142
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile.func1
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:105
runtime.gopanic
        runtime/panic.go:783
runtime.panicmem
        runtime/panic.go:262
runtime.sigpanic
        runtime/signal_unix.go:925
k8s.io/apiserver/pkg/cel/openapi.isExtension
        k8s.io/apiserver@v0.34.1/pkg/cel/openapi/extensions.go:28
k8s.io/apiserver/pkg/cel/openapi.isXEmbeddedResource
        k8s.io/apiserver@v0.34.1/pkg/cel/openapi/extensions.go:39
k8s.io/apiserver/pkg/cel/openapi.(*Schema).IsXEmbeddedResource
        k8s.io/apiserver@v0.34.1/pkg/cel/openapi/adaptor.go:192
github.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata
        github.com/kubernetes-sigs/kro/pkg/cel/schemas.go:107
github.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata
        github.com/kubernetes-sigs/kro/pkg/cel/schemas.go:92
github.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata
        github.com/kubernetes-sigs/kro/pkg/cel/schemas.go:143
github.com/kubernetes-sigs/kro/pkg/cel.SchemaDeclTypeWithMetadata
        github.com/kubernetes-sigs/kro/pkg/cel/schemas.go:143
github.com/kubernetes-sigs/kro/pkg/cel.DefaultEnvironment
        github.com/kubernetes-sigs/kro/pkg/cel/environment.go:106
github.com/kubernetes-sigs/kro/pkg/cel.TypedEnvironment
        github.com/kubernetes-sigs/kro/pkg/cel/environment.go:148
github.com/kubernetes-sigs/kro/pkg/graph.buildStatusSchema
        github.com/kubernetes-sigs/kro/pkg/graph/builder.go:610
github.com/kubernetes-sigs/kro/pkg/graph.(*Builder).buildInstanceResource
        github.com/kubernetes-sigs/kro/pkg/graph/builder.go:486
github.com/kubernetes-sigs/kro/pkg/graph.(*Builder).NewResourceGraphDefinition
        github.com/kubernetes-sigs/kro/pkg/graph/builder.go:183
github.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition.(*ResourceGraphDefinitionReconciler).reconcileResourceGraphDefinitionGraph
        github.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition/controller_reconcile.go:132
github.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition.(*ResourceGraphDefinitionReconciler).reconcileResourceGraphDefinition
        github.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition/controller_reconcile.go:47
github.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition.(*ResourceGraphDefinitionReconciler).Reconcile
        github.com/kubernetes-sigs/kro/pkg/controller/resourcegraphdefinition/controller.go:172
sigs.k8s.io/controller-runtime/pkg/reconcile.(*objectReconcilerAdapter[...]).Reconcile
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile/reconcile.go:154
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:116
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:303
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:263
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.2
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:224
2025-11-25T11:54:21.172Z        ERROR   rgd-controller  Reconciler error        {"controller": "ResourceGraphDefinition", "controllerGroup": "kro.run", "controllerKind": "ResourceGraphDefinition", "name": "advanced-app", "reconcileID": "1f27285b-be4d-4eeb-a8cd-7809b551b680", "error": "panic: runtime error: invalid memory address or nil pointer dereference [recovered]"}
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:316
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:263
sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func2.2
        sigs.k8s.io/controller-runtime@v0.19.0/pkg/internal/controller/controller.go:224

```

* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request
* Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
* If you are interested in working on this issue or have submitted a pull request, please leave a comment

### Which option describes the most your issue?

ResourceGraphDefinition (Create, Update, Deletion)

Hints from issue comments:
@jakobmoellerdev: Hey there, thanks for spotting this. I believe this is a malbehavior on KROs type conversion. I will take a look at this in the next few days@jakobmoellerdev: this was actually an upstream bug thats fixed in apiserver already but has not yet landed in a versioned release. i pushed a hotfix for this in the meantime.@jakobmoellerdev: /assign

Solution patch:
diff --git a/pkg/cel/schemas.go b/pkg/cel/schemas.go
index 0719e5b..cf593cc 100644
--- a/pkg/cel/schemas.go
+++ b/pkg/cel/schemas.go
@@ -22,9 +22,9 @@ import (
 
 	"github.com/google/cel-go/cel"
 	"github.com/google/cel-go/common/types"
-	"k8s.io/apiserver/pkg/cel/common"
-
 	apiservercel "k8s.io/apiserver/pkg/cel"
+	"k8s.io/apiserver/pkg/cel/common"
+	celopenapi "k8s.io/apiserver/pkg/cel/openapi"
 )
 
 // XKubernetesPreserveUnknownFields is the key for the named open api extension, declaration type metadata field
@@ -104,7 +104,20 @@ func SchemaDeclTypeWithMetadata(s common.Schema, isResourceRoot bool) *apiserver
 		return nil
 	case "object":
 		if s.AdditionalProperties() != nil && s.AdditionalProperties().Schema() != nil {
-			propsType := SchemaDeclTypeWithMetadata(s.AdditionalProperties().Schema(), s.AdditionalProperties().Schema().IsXEmbeddedResource())
+			additional := s.AdditionalProperties().Schema()
+			var propsType *apiservercel.DeclType
+			// TODO(jakobmoellerdev): revisit this once upstream is fixed
+			// upstream bug in apiserver where SchemaOrBool returns a non nil pointer with nil content
+			// if the underlying schema
+			// is nil instead of returning nil
+			// has been fixed in upstream api server but not part of main codebase as of kubernetes 1.34
+			// https://github.com/kubernetes/apiserver/blob/master/pkg/cel/openapi/adaptor.go
+			// https://github.com/kubernetes/apiserver/blob/v0.34.2/pkg/cel/openapi/adaptor.go
+			if openapi, ok := additional.(*celopenapi.Schema); ok {
+				if openapi.Schema != nil {
+					propsType = SchemaDeclTypeWithMetadata(openapi, s.AdditionalProperties().Schema().IsXEmbeddedResource())
+				}
+			}
 			mt := apiservercel.NewMapType(apiservercel.StringType, apiservercel.DynType, math.MaxInt)
 			if propsType != nil {
 				var maxProperties int64



Test patch:
diff --git a/test/integration/suites/core/unknown_fields_test.go b/test/integration/suites/core/unknown_fields_test.go
index 57c1372..b416e2a 100644
--- a/test/integration/suites/core/unknown_fields_test.go
+++ b/test/integration/suites/core/unknown_fields_test.go
@@ -307,4 +307,121 @@ var _ = Describe("Unknown Fields", func() {
 			}, 10*time.Second, time.Second).WithContext(ctx).Should(Succeed())
 		})
 	})
+
+	Context("Second RGD status referencing first RGD status", func() {
+		It("should not panic when the second RGD embeds the first RGD", func(ctx SpecContext) {
+			basicRGD := generator.NewResourceGraphDefinition(
+				"basic-app-"+rand.String(4),
+				generator.WithSchema(
+					"BasicApp", "v1alpha1",
+					map[string]any{
+						"name":     "string",
+						"image":    "string | default=\"nginx\"",
+						"replicas": "integer | default=3",
+					},
+					map[string]any{
+						"deploymentConditions": "${deployment.status.conditions}",
+						"availableReplicas":    "${deployment.status.availableReplicas}",
+					},
+				),
+				generator.WithResource(
+					"deployment",
+					map[string]any{
+						"apiVersion": "apps/v1",
+						"kind":       "Deployment",
+						"metadata": map[string]any{
+							"name": "${schema.spec.name}",
+						},
+						"spec": map[string]any{
+							"replicas": "${schema.spec.replicas}",
+							"selector": map[string]any{
+								"matchLabels": map[string]any{
+									"app": "${schema.spec.name}",
+								},
+							},
+							"template": map[string]any{
+								"metadata": map[string]any{
+									"labels": map[string]any{
+										"app": "${schema.spec.name}",
+									},
+								},
+								"spec": map[string]any{
+									"containers": []any{
+										map[string]any{
+											"name":  "${schema.spec.name}",
+											"image": "${schema.spec.image}",
+											"ports": []any{
+												map[string]any{
+													"containerPort": 80,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+					nil,
+					nil,
+				),
+			)
+
+			Expect(env.Client.Create(ctx, basicRGD)).To(Succeed())
+
+			Eventually(func(g Gomega, ctx SpecContext) {
+				g.Expect(env.Client.Get(ctx,
+					types.NamespacedName{Name: basicRGD.Name},
+					basicRGD,
+				)).To(Succeed())
+				g.Expect(basicRGD.Status.State).
+					To(Equal(krov1alpha1.ResourceGraphDefinitionStateActive))
+			}, 20*time.Second, time.Second).WithContext(ctx).Should(Succeed())
+
+			advancedRGD := generator.NewResourceGraphDefinition(
+				"advanced-app-"+rand.String(4),
+				generator.WithSchema(
+					"AdvancedApp", "v1alpha1",
+					map[string]any{
+						"name":     "string",
+						"replicas": "integer | default=3",
+						"image":    "string | default=\"nginx\"",
+					},
+					map[string]any{
+						// This line forces the second RGD to load the BasicApp status schema
+						"deploymentInfo": "${rgddep.status}",
+					},
+				),
+				generator.WithResource(
+					"rgddep",
+					map[string]any{
+						"apiVersion": "kro.run/v1alpha1",
+						"kind":       "BasicApp",
+						"metadata": map[string]any{
+							"name": "my-advanced-app-${schema.spec.name}",
+						},
+						"spec": map[string]any{
+							"name":     "${schema.spec.name}",
+							"replicas": "${schema.spec.replicas}",
+							"image":    "${schema.spec.image}",
+						},
+					},
+					nil,
+					nil,
+				),
+			)
+
+			Expect(env.Client.Create(ctx, advancedRGD)).To(Succeed())
+
+			Eventually(func(g Gomega, ctx SpecContext) {
+				g.Expect(env.Client.Get(ctx,
+					types.NamespacedName{Name: advancedRGD.Name},
+					advancedRGD,
+				)).To(Succeed())
+
+				// IF THE PANIC STILL EXISTS: this never reaches Active
+				g.Expect(advancedRGD.Status.State).
+					To(Equal(krov1alpha1.ResourceGraphDefinitionStateActive))
+			}, 20*time.Second, time.Second).WithContext(ctx).Should(Succeed())
+		})
+	})
 })