CertificateRequest with Ready=False/Failed and no failureTime is never replaced when the private key is reused

Author: thc1006Created Sep 10, 2026Updated Sep 10, 2026
Labelskind/bug

Describe the bug:

A CertificateRequest with Ready=False, reason Failed, and no status.failureTime is never replaced when the private key is reused. The Certificate's failedIssuanceAttempts climbs on every retry and the same request stays in place.

metav1.Time.Before returns false on a nil receiver, so the request manager's previous-issuance check never fires:

https://github.com/cert-manager/cert-manager/blob/f2cafccba8ef502a08802b720a6deccf67530663/pkg/controller/certificates/requestmanager/requestmanager_controller.go#L284

The issuing controller's matching guard requires FailureTime != nil, so it skips and the request reaches failIssueCertificate on every issuance:

https://github.com/cert-manager/cert-manager/blob/f2cafccba8ef502a08802b720a6deccf67530663/pkg/controller/certificates/issuing/issuing_controller.go#L305-L306

Found by @wallrj while reviewing #9287, which fixes the mirror case of a failureTime with no Ready condition. Raised separately because the fix is not the same one.

Expected behaviour:

The request is replaced on the next attempt, the way a Ready=False/Failed request with a failureTime is.

Steps to reproduce the bug:

Set Ready=False with reason Failed on the CertificateRequest for the current revision and leave status.failureTime unset. No in-tree issuer produces this: Reporter.Failed sets both fields in one update.

Anything else we need to know?:

I ran the trigger, keymanager, request manager and issuing controllers against an envtest apiserver, marked the first request that way, and crossed the retry backoff three times:

rotationPolicy failedIssuanceAttempts after 3 retries same CertificateRequest object
default (Always) 1 no, replaced on the first retry
Never 4 yes

With the default policy the keymanager rotates the next private key, the existing spec and key mismatch check removes the request, and the issuance recovers. With a reused key nothing removes it.

Deleting when failureTime is nil is not the fix on its own. The request manager can run before the issuing controller and would replace the request before the failure is recorded, which loses the backoff.

Environment details:

  • Kubernetes version: envtest 1.36.2
  • cert-manager version: master (f2cafccb)
  • Install method: none, the controllers were run directly against envtest

/kind bug

with claude opus-5

Source: cert-manager/cert-manager