{"record":{"id":"d0aa7361b5312105","repo":"dapr/dapr","slug":"failed-to-patch-webhook-in-crd-q-v","errorCode":null,"errorMessage":"failed to patch webhook in CRD %q: %v","messagePattern":"failed to patch webhook in CRD %q: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/operator/operator.go","lineNumber":426,"sourceCode":"\t\t\tPath  string `json:\"path\"`\n\t\t\tValue any    `json:\"value\"`\n\t\t}\n\t\tpayload := []patchValue{{\n\t\t\tOp:    \"replace\",\n\t\t\tPath:  \"/spec/conversion/webhook/clientConfig/service/namespace\",\n\t\t\tValue: security.CurrentNamespace(),\n\t\t}, {\n\t\t\tOp:    \"replace\",\n\t\t\tPath:  \"/spec/conversion/webhook/clientConfig/caBundle\",\n\t\t\tValue: caBundle,\n\t\t}}\n\n\t\tpayloadJSON, err := json.Marshal(payload)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not marshal webhook spec: %w\", err)\n\t\t}\n\t\tif _, err := crdClient.Patch(ctx, crdName, types.JSONPatchType, payloadJSON, v1.PatchOptions{}); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to patch webhook in CRD %q: %v\", crdName, err)\n\t\t}\n\n\t\tlog.Infof(\"Successfully patched webhook in CRD %q\", crdName)\n\t}\n\n\treturn nil\n}\n\nfunc buildScheme(opts Options) (*runtime.Scheme, error) {\n\tbuilders := []func(*runtime.Scheme) error{\n\t\tclientgoscheme.AddToScheme,\n\t\tcomponentsapi.AddToScheme,\n\t\tconfigurationapi.AddToScheme,\n\t\tresiliencyapi.AddToScheme,\n\t\thttpendpointsapi.AddToScheme,\n\t\tmcpserverapi.AddToScheme,\n\t\tsubscriptionsapiV1alpha1.AddToScheme,\n\t\tsubapi.AddToScheme,","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/dapr/dapr/blob/74ad41702745709bb15fe2114ff693b8c59bc3cc/pkg/operator/operator.go#L408-L444","documentation":"Applies a JSON patch (replace /spec/conversion/webhook/clientConfig/service/namespace and .../caBundle) to the CRD. It fails when the API server rejects the patch: a \"replace\" against a path that does not exist (clientConfig.Service nil — URL-based clientConfig, the sub-field variant of error 412), 403 from RBAC denying patch on customresourcedefinitions, optimistic-concurrency conflicts, or transient API server errors.","triggerScenarios":"CRD whose conversion clientConfig uses url instead of service (replace path missing); operator service account missing patch permission on apiextensions.k8s.io; concurrent CRD updates by other controllers; API server briefly unavailable.","commonSituations":"RBAC-minimized clusters; mixed installs where some CRDs were customized with URL-based webhooks; operators running while CRDs are being modified by GitOps.","solutions":["Confirm the CRD's conversion clientConfig is service-based (kubectl get crd subscriptions.dapr.io -o yaml); if not, reinstall the stock CRDs.","Grant patch on apiextensions.k8s.io customresourcedefinitions to the operator service account.","Inspect the wrapped API error: 4xx means fix the CRD/RBAC; 5xx/timeouts usually clear on operator restart since the patch reruns at startup."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"func conversionPatchable(crd *apiextensionsv1.CustomResourceDefinition) bool {\n\tc := crd.Spec.Conversion\n\treturn c != nil && c.Webhook != nil && c.Webhook.ClientConfig != nil && c.Webhook.ClientConfig.Service != nil\n}","typeGuard":"func patchRetryable(err error) bool {\n\treturn apierrors.IsConflict(err) || apierrors.IsInternalError(err) || apierrors.IsTimeout(err) || apierrors.IsServerTimeout(err)\n}","tryCatchPattern":"if _, err := crdClient.Patch(ctx, crdName, types.JSONPatchType, payloadJSON, v1.PatchOptions{}); err != nil {\n\tif apierrors.IsForbidden(err) {\n\t\t// permanent: grant patch on customresourcedefinitions\n\t} else if apierrors.IsInvalid(err) || strings.Contains(err.Error(), \"replace\") {\n\t\t// CRD lacks the service-based clientConfig path — reinstall CRDs\n\t} else if patchRetryable(err) {\n\t\t// transient: operator retries on next restart / CA rotation\n\t}\n\treturn fmt.Errorf(\"failed to patch webhook in CRD %q: %v\", crdName, err)\n}","preventionTips":["Keep apiextensions patch RBAC in the operator ClusterRole.","Do not convert the Dapr CRD conversion clientConfig to URL-based form.","Let the operator restart after CA rotation so the patch re-applies the new bundle."],"tags":["kubernetes","crd","patch","rbac","webhook","operator"],"backgroundTag":null,"analyzedSha":"74ad41702745709bb15fe2114ff693b8c59bc3cc","analyzedAt":"2026-08-16T04:22:26.543Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}