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

  1. Confirm the CRD's conversion clientConfig is service-based (kubectl get crd subscriptions.dapr.io -o yaml); if not, reinstall the stock CRDs.
  2. Grant patch on apiextensions.k8s.io customresourcedefinitions to the operator service account.
  3. 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

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


AI-assisted analysis of dapr/dapr@74ad417027 (2026-08-16). Data as JSON: /api/errors/d0aa7361b5312105. Report an issue: GitHub.