kubernetes/kubernetes · warning

can't recheck DeletionTimestamp: %v

Error message

can't recheck DeletionTimestamp: %v

What it means

Returned by the CanAdopt closure produced by RecheckDeletionTimestamp when its getObject callback errors. The closure re-GETs the parent controller object to check for a fresh DeletionTimestamp; if the GET fails (not-found, network, conflict), this error is returned and blocks all adoptions by that ControllerRefManager instance for the current sync pass.

Source

Thrown at pkg/controller/controller_ref_manager.go:395

			// Invalid error will be returned in two cases: 1. the ReplicaSet
			// has no owner reference, 2. the uid of the ReplicaSet doesn't
			// match, which means the ReplicaSet is deleted and then recreated.
			// In both cases, the error can be ignored.
			return nil
		}
	}
	return err
}

// RecheckDeletionTimestamp returns a CanAdopt() function to recheck deletion.
//
// The CanAdopt() function calls getObject() to fetch the latest value,
// and denies adoption attempts if that object has a non-nil DeletionTimestamp.
func RecheckDeletionTimestamp(getObject func(context.Context) (metav1.Object, error)) func(context.Context) error {
	return func(ctx context.Context) error {
		obj, err := getObject(ctx)
		if err != nil {
			return fmt.Errorf("can't recheck DeletionTimestamp: %v", err)
		}
		if obj.GetDeletionTimestamp() != nil {
			return fmt.Errorf("%v/%v has just been deleted at %v", obj.GetNamespace(), obj.GetName(), obj.GetDeletionTimestamp())
		}
		return nil
	}
}

// ControllerRevisionControllerRefManager is used to manage controllerRef of ControllerRevisions.
// Three methods are defined on this object 1: Classify 2: AdoptControllerRevision and
// 3: ReleaseControllerRevision which are used to classify the ControllerRevisions into appropriate
// categories and accordingly adopt or release them. See comments on these functions
// for more details.
type ControllerRevisionControllerRefManager struct {
	BaseControllerRefManager
	controllerKind schema.GroupVersionKind
	crControl      ControllerRevisionControlInterface
}

View on GitHub (pinned to 94c1367642)

Solutions

  1. Treat as transient: the next sync pass creates a new ControllerRefManager (and thus a new CanAdopt) which re-checks.
  2. If getObject uses a lister and persistently fails, restart the controller to reset the informer cache.
  3. Verify the parent object exists with the expected UID; if it was deleted, this is expected behavior.
  4. If getObject uses a live client, confirm apiserver reachability.

Example fix

// before
return func(ctx context.Context) error {
    obj, err := getObject(ctx)
    if err != nil {
        return fmt.Errorf("can't recheck DeletionTimestamp: %v", err)
    }
    ...
}
// after: treat not-found as a benign 'already deleted' rather than an opaque error
return func(ctx context.Context) error {
    obj, err := getObject(ctx)
    if err != nil {
        if apierrors.IsNotFound(err) {
            return fmt.Errorf("can't recheck DeletionTimestamp: object has been deleted")
        }
        return fmt.Errorf("can't recheck DeletionTimestamp: %v", err)
    }
    ...
}
Defensive patterns

Strategy: retry

Validate before calling

// Verify parent object is reachable before constructing the CanAdopt closure
if _, err := getObject(ctx); err != nil {
    return fmt.Errorf("parent not reachable for DeletionTimestamp recheck: %v", err)
}

Try / catch

// In the controller sync loop, treat CanAdopt failures as transient
if _, _, err := m.ClaimReplicaSets(ctx, sets); err != nil {
    if strings.Contains(err.Error(), "can't recheck DeletionTimestamp") {
        return err // requeue; next pass rebuilds the manager
    }
}

Prevention

When it happens

Trigger: RecheckDeletionTimestamp returns a closure; the closure calls getObject(ctx); getObject errors. Common callers pass a lister Get or a live client Get. Failures: parent not found (deleted), apiserver unreachable, watch cache stale returning an error.

Common situations: Parent controller object (Deployment/ReplicaSet) deleted between sync start and the CanAdopt call; informer cache returning a stale/missing object; transient apiserver error; parent recreated with a new UID causing not-found on the old UID.

Related errors


AI-assisted analysis of kubernetes/kubernetes@94c1367642 (2026-08-08). Data as JSON: /api/errors/3a0d8f7791eac15a. Report an issue: GitHub.