dapr/dapr · error
failed to patch webhook in CRD %q: %v
Error message
failed to patch webhook in CRD %q: %v
What it means
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.
Source
Thrown at pkg/operator/operator.go:426
Path string `json:"path"`
Value any `json:"value"`
}
payload := []patchValue{{
Op: "replace",
Path: "/spec/conversion/webhook/clientConfig/service/namespace",
Value: security.CurrentNamespace(),
}, {
Op: "replace",
Path: "/spec/conversion/webhook/clientConfig/caBundle",
Value: caBundle,
}}
payloadJSON, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("could not marshal webhook spec: %w", err)
}
if _, err := crdClient.Patch(ctx, crdName, types.JSONPatchType, payloadJSON, v1.PatchOptions{}); err != nil {
return fmt.Errorf("failed to patch webhook in CRD %q: %v", crdName, err)
}
log.Infof("Successfully patched webhook in CRD %q", crdName)
}
return nil
}
func buildScheme(opts Options) (*runtime.Scheme, error) {
builders := []func(*runtime.Scheme) error{
clientgoscheme.AddToScheme,
componentsapi.AddToScheme,
configurationapi.AddToScheme,
resiliencyapi.AddToScheme,
httpendpointsapi.AddToScheme,
mcpserverapi.AddToScheme,
subscriptionsapiV1alpha1.AddToScheme,
subapi.AddToScheme,View on GitHub (pinned to 74ad417027)
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.
Defensive patterns
Strategy: retry
Validate before calling
func conversionPatchable(crd *apiextensionsv1.CustomResourceDefinition) bool {
c := crd.Spec.Conversion
return c != nil && c.Webhook != nil && c.Webhook.ClientConfig != nil && c.Webhook.ClientConfig.Service != nil
} Type guard
func patchRetryable(err error) bool {
return apierrors.IsConflict(err) || apierrors.IsInternalError(err) || apierrors.IsTimeout(err) || apierrors.IsServerTimeout(err)
} Try / catch
if _, err := crdClient.Patch(ctx, crdName, types.JSONPatchType, payloadJSON, v1.PatchOptions{}); err != nil {
if apierrors.IsForbidden(err) {
// permanent: grant patch on customresourcedefinitions
} else if apierrors.IsInvalid(err) || strings.Contains(err.Error(), "replace") {
// CRD lacks the service-based clientConfig path — reinstall CRDs
} else if patchRetryable(err) {
// transient: operator retries on next restart / CA rotation
}
return fmt.Errorf("failed to patch webhook in CRD %q: %v", crdName, err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: RBAC-minimized clusters; mixed installs where some CRDs were customized with URL-based webhooks; operators running while CRDs are being modified by GitOps.
Related errors
- could not get CRD %q: %v
- cache failed to sync
- error listing http endpoints: %w
- pod metadata cache failed to sync
- configuration %s not found
AI-assisted analysis of dapr/dapr@74ad417027 (2026-08-16).
Data as JSON: /api/errors/d0aa7361b5312105.
Report an issue: GitHub.