Free — no signup required

Handling "Not Found" Errors

2 min read

The First Edge Case Every Controller Must Handle

The first thing your Reconcile loop does is fetch the object by name. But what if the object no longer exists by the time your function runs?

var myResource myapi.MyResource
if err := r.Get(ctx, req.NamespacedName, &myResource); err != nil {
    if errors.IsNotFound(err) {
        // Object is gone. Nothing to do.
        return ctrl.Result{}, nil
    }
    // Something else went wrong (network error, permission denied, etc.)
    return ctrl.Result{}, err
}

This pattern is so common that controller-runtime provides a helper: client.IgnoreNotFound(err), which returns nil if the error is a "not found" error and passes through any other error.

Why return nil (success) when the object is not found?

Because there is nothing to reconcile. The object is gone. If you return an error, the controller framework will retry the reconcile with exponential backoff — endlessly looking for an object that no longer exists. This wastes resources and pollutes your logs.

The "not found" case is not an error in the traditional sense. It is a valid terminal state: the user deleted the resource, and your controller has nothing left to do (assuming you handle cleanup via Finalizers, covered later).

The important distinction: "Not Found" is a business logic outcome — handle it explicitly and return success. Any other error (network timeout, RBAC permission denied, API server unavailable) is a genuine failure — return the error so the framework retries.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free