Initializing Enclave...

Fixing 'invalid value: 0: must be greater than 0' in Kubernetes CRD Validation Webhooks

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–15 mins


TL;DR

  • What broke: A Custom Resource or CRD spec contains a field set to 0, but the OpenAPI v3 schema in the CRD (or the validating webhook logic) enforces minimum: 1 — the admission webhook hard-rejects the object before it reaches the API server storage.
  • How to fix it: Find the offending field in your CR manifest, set it to a valid non-zero integer, OR audit your CRD schema's minimum constraint if 0 is a legitimate value for your use case.
  • Shortcut: Use our Client-Side Sandbox below to auto-refactor this — paste your failing CR or CRD schema and get corrected YAML instantly.

The Incident (What does the error mean?)

Raw webhook rejection from kubectl apply:

Error from server: error when creating "myresource.yaml":
admission webhook "validate.myoperator.io" denied the request:
Invalid value: 0: must be greater than 0 (spec.replicas)

Or from the API server's built-in CRD structural schema validation:

The MyResource "my-app" is invalid:
spec.replicas: Invalid value: 0: must be greater than 0

Immediate consequence: The resource object is never persisted to etcd. Your operator never reconciles it. If this is a rollout, the deployment is silently blocked — no pods are created, no error surfaces in the workload itself, only in the kubectl apply output or CI pipeline logs.


The Attack Vector / Blast Radius

This is not just a typo problem. There are two distinct failure modes:

1. Operator schema is too strict (your schema, your bug) You defined minimum: 1 in the CRD OpenAPI schema for a field like replicas, timeout, maxRetries, or port. A developer legitimately sets replicas: 0 to scale down a workload — a valid operational intent — and the webhook hard-blocks it. Blast radius: Any automation (GitOps, Argo CD, Flux) that attempts to apply this manifest will fail in a reconciliation loop. If your operator manages critical infrastructure, this can cause a cascade where the desired state can never be reached.

2. Validating webhook has hardcoded logic (webhook code bug) The webhook's Go/Python validation handler explicitly checks if value <= 0 { deny }. A misconfigured Helm chart or an environment-specific override passes 0 for a field like minAvailable, terminationGracePeriodSeconds, or a custom threshold. Blast radius: Every kubectl apply, every Argo CD sync, every Helm upgrade for that resource class is blocked until the webhook is patched or the value is corrected.

Why this is dangerous beyond inconvenience: If the failurePolicy on the ValidatingWebhookConfiguration is set to Fail (the secure default), and your webhook pod crashes or becomes unreachable, all matching resource mutations cluster-wide are blocked. A misconfigured schema combined with a failing webhook pod is a cluster-level denial of service.


How to Fix It

Identify the offending field first

# Get the full rejection reason
kubectl apply -f myresource.yaml 2>&1

# Inspect the CRD schema for minimum constraints
kubectl get crd myresources.myoperator.io -o jsonpath='{.spec.versions[*].schema.openAPIV3Schema}' | jq '.. | .minimum? // empty'

Basic Fix — Correct the CR value

If 0 is genuinely invalid for this field (e.g., replicas: 0 makes no sense for your operator), just fix the manifest:

apiVersion: myoperator.io/v1alpha1
kind: MyResource
metadata:
  name: my-app
spec:
- replicas: 0
+ replicas: 1

Enterprise Best Practice — Fix the CRD schema if 0 is a valid value

If your operator should support 0 (e.g., scale-to-zero semantics), the minimum: 1 constraint in the CRD is the real bug. Patch the CRD schema:

# In your CRD YAML under spec.versions[].schema.openAPIV3Schema
properties:
  replicas:
    type: integer
    description: "Desired number of replicas. Set to 0 to scale down."
-   minimum: 1
+   minimum: 0
    default: 1

And if the constraint lives in the webhook handler code (Go example):

func validateMyResource(obj *v1alpha1.MyResource) error {
-  if obj.Spec.Replicas <= 0 {
-    return fmt.Errorf("invalid value: %d: must be greater than 0", obj.Spec.Replicas)
-  }
+  if obj.Spec.Replicas < 0 {
+    return fmt.Errorf("invalid value: %d: replicas cannot be negative", obj.Spec.Replicas)
+  }
   return nil
}

After patching the CRD, force a schema re-apply:

kubectl apply -f crds/myresource-crd.yaml
# Verify the constraint is gone
kubectl get crd myresources.myoperator.io -o json | jq '.spec.versions[0].schema.openAPIV3Schema.properties.spec.properties.replicas'

💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Validate CRs against the CRD schema before kubectl apply using kubeconform:

# Install kubeconform
kubeconform -schema-location default \
  -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
  myresource.yaml

2. OPA/Gatekeeper ConstraintTemplate to enforce non-zero fields with context:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: requirepositivereplicas
spec:
  crd:
    spec:
      names:
        kind: RequirePositiveReplicas
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package requirepositivereplicas
        violation[{"msg": msg}] {
          input.review.object.spec.replicas < 1
          msg := sprintf("replicas must be >= 1, got %v", [input.review.object.spec.replicas])
        }

3. Helm lint + schema validation in your pipeline:

# .github/workflows/validate.yaml
- name: Helm lint
  run: helm lint ./charts/myoperator --strict

- name: Kubeconform CRD validation
  run: |
    kubeconform -strict -summary ./manifests/

4. Set failurePolicy: Ignore only in dev namespaces. Never in production. A crashing webhook with failurePolicy: Fail will block all resource creation in matching namespaces.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: myoperator-validator
webhooks:
  - name: validate.myoperator.io
    failurePolicy: Fail   # Correct for prod
    namespaceSelector:
      matchExpressions:
        - key: environment
          operator: NotIn
          values: ["dev", "sandbox"]

Related Diagnostics

"Part of the Syntax Utility Matrix."

View all 153 Syntax Tools →