Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 8 min read

Kubernetes Error Guide: 'no matches for kind in version' — Fix Unrecognized Resources

Quick answer

Fix the 'no matches for kind in version' error in Kubernetes: install missing CRDs, use the right apiVersion, and handle removed API groups on apply.

  • #kubernetes
  • #troubleshooting
  • #errors
  • #crds
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

kubectl apply fails with unable to recognize ...: no matches for kind "..." in version "...". This means the API server does not know about the resource type you are trying to create. Kubernetes can only accept a kind/apiVersion pair that is registered — either a built-in type, or a CustomResourceDefinition (CRD) that has been installed. If the CRD is not installed, or you used a wrong/removed apiVersion, the server has nothing to match your object against and rejects it before storing anything.

The literal error looks like this:

error: unable to recognize "app.yaml": no matches for kind "Certificate" in version "cert-manager.io/v1"

You will hit this most often when: applying a CR before its operator/CRD is installed, ordering matters in a manifest bundle, using an apiVersion that was removed in your cluster’s Kubernetes version, or targeting a cluster that simply does not have the add-on. The fix is to make the type known — install the CRD, correct the apiVersion, or apply resources in the right order.

Symptoms

  • kubectl apply returns no matches for kind "<Kind>" in version "<group>/<version>".
  • Only the custom/removed resources fail; built-in resources apply fine.
  • The error is at apply time; nothing is created for that object.
  • kubectl api-resources does not list the kind, or lists a different version.
kubectl apply -f app.yaml
service/api created
deployment.apps/api created
error: unable to recognize "app.yaml": no matches for kind "Certificate" in version "cert-manager.io/v1"

Common Root Causes

1. The CRD (operator) is not installed

The most common cause: you are applying a custom resource (Certificate, Rollout, ServiceMonitor) but the controller that owns its CRD was never installed, so the type does not exist.

kubectl get crd | grep cert-manager
(no output)

Install the operator/CRDs first, then apply the custom resource.

2. Apply ordering — CRD and CR in the same bundle

If a single kubectl apply -f dir/ includes both a CRD and a CR that uses it, the CR can be evaluated before the CRD is fully established, producing this error transiently.

kubectl apply -f manifests/

Apply the CRD first (or re-run apply so the second pass finds the now-registered type).

3. Wrong or removed apiVersion

The kind exists but under a different apiVersion. A manifest written for an older cluster may reference a version that was removed (e.g. batch/v1beta1 CronJob, networking.k8s.io/v1beta1 Ingress, policy/v1beta1 PodDisruptionBudget).

kubectl api-resources | grep -i cronjob
cronjobs   cj   batch/v1   true   CronJob

Update the manifest to the version the cluster serves (batch/v1).

4. Wrong cluster / context

You are pointed at a cluster that does not have the add-on installed (e.g. staging has cert-manager, the sandbox does not).

kubectl config current-context

5. A typo in group or kind

A misspelled group (cert-manger.io) or kind means nothing matches.

Diagnostic Workflow

Step 1: Read the exact kind and version

kubectl apply -f app.yaml

The message gives you kind and group/version — those are what must be registered.

Step 2: Check whether the type is served

kubectl api-resources | grep -i <kind>
kubectl api-versions | grep <group>

No match means the CRD is missing or the version is wrong.

Step 3: Check for the CRD

kubectl get crd | grep <group>

Step 4: Confirm the correct apiVersion for a built-in

kubectl explain <kind> | head -3

kubectl explain shows the currently served version for that kind.

Step 5: Install/fix and re-apply

kubectl apply -f <operator-or-crd>.yaml
kubectl wait --for condition=established crd/<name> --timeout=60s
kubectl apply -f app.yaml

Example Root Cause Analysis

A deploy pipeline applies an app bundle to a fresh cluster and fails on the TLS resource:

kubectl apply -f k8s/
deployment.apps/web created
service/web created
error: unable to recognize "k8s/certificate.yaml": no matches for kind "Certificate" in version "cert-manager.io/v1"

Checking whether the type exists:

kubectl api-resources | grep -i certificate
(no output)

And the CRD:

kubectl get crd | grep cert-manager
(no output)

cert-manager is not installed on this cluster, so Certificate is an unknown kind. Installing cert-manager (which registers the CRDs), waiting for them to establish, then re-applying resolves it:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.0/cert-manager.yaml
kubectl wait --for condition=established crd/certificates.cert-manager.io --timeout=60s
kubectl apply -f k8s/certificate.yaml
certificate.cert-manager.io/web-tls created

The type is now registered and the resource applies cleanly.

Prevention Best Practices

  • Install CRDs/operators before the custom resources that use them; make CRD installation a distinct, earlier pipeline stage.
  • Use kubectl wait --for condition=established crd/<name> so applies do not race a not-yet-registered CRD.
  • Pin manifests to apiVersions your target cluster version actually serves; scan for removed APIs with kubectl-convert, pluto, or kubent before upgrades.
  • Keep add-on manifests (cert-manager, Prometheus, Argo Rollouts) versioned alongside the app so a fresh cluster gets both.
  • Verify kubectl config current-context in CI so you never apply against a cluster missing the add-on. See more in Kubernetes & Helm guides.

Quick Command Reference

# See the unrecognized kind/version
kubectl apply -f app.yaml

# Is the type served?
kubectl api-resources | grep -i <kind>
kubectl api-versions | grep <group>

# Is the CRD installed?
kubectl get crd | grep <group>

# Correct apiVersion for a built-in
kubectl explain <kind> | head -3

# Install CRDs, wait, then apply the CR
kubectl apply -f <operator>.yaml
kubectl wait --for condition=established crd/<name> --timeout=60s
kubectl apply -f app.yaml

Conclusion

no matches for kind "..." in version "..." means the API server does not recognize the resource type. The usual causes:

  1. The CRD/operator that defines the type is not installed.
  2. A CRD and its CR are applied together and race.
  3. A wrong or removed apiVersion.
  4. The wrong cluster/context (add-on absent).
  5. A typo in the group or kind.

Check kubectl api-resources and kubectl get crd to confirm the type is registered, install the CRD first, and pin the correct apiVersion. For ad-hoc triage, the free incident assistant can turn this apply error into the exact CRD or apiVersion fix.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.