Skip to content

Three Repository resources never retry after an async update failure, so a fixed root cause cannot reach them #137

Description

@devantler

🤖 Generated by the Agentic Engineer

Evidence

#112 restored the repository write path (#136, merged 2026-08-06T01:37:50Z, artifact 1.22.1). Every resource that attempted an update afterwards succeeded:

Repository LastAsyncOperation after the fix
aws Success 2026-08-06T01:39:11Z
doggy-countdown Success 01:39:09Z
kyverno-policies Success 01:39:11Z
provider-upjet-unifi Success 01:39:10Z

Three did not, and still report ReconcileError with the old 422 message:

Repository last update attempt age at 2026-08-06
agent-skills 2026-07-20T00:32:52Z 17 days
platform-tenant-template 2026-08-02T18:54:05Z 3 days
agent-plugins 2026-08-03T19:45:36Z 2 days

Problem

The Synced message on these three is the cached result of that old attempt, not a fresh rejection — the underlying cause it names was fixed and demonstrably no longer rejects anyone else. They are not being rejected; they are not trying.

That matters beyond these three repositories: it means a resource can enter a state where fixing the root cause is not enough to recover it, and the stale condition message actively misleads whoever looks — it points at a cause that is already resolved. Any future repository-settings failure can strand a resource the same way.

⚠️ Synced.lastTransitionTime is rewritten roughly every 60s whether or not anything happened, so it cannot be used to tell a fresh failure from a stale one. Use LastAsyncOperation.lastTransitionTime, which only moves when an operation actually completes.

Observation worth checking first

The three carry only the crossplane.io/external-name annotation. Healed resources such as kyverno-policies also carry crossplane.io/external-create-pending, crossplane.io/external-create-succeeded and crossplane.io/external-create-failed. Whether that is the cause or a side effect of the same history is unverified — it is the first thing to test, not a conclusion.

Suggested approach

  • Establish why no reconcile issues an update: a stuck async-operation record, an exponential backoff that has grown unbounded, or the resource being considered up to date while the condition says otherwise.
  • Prefer a fix that recovers automatically. A manual nudge (annotation bump, provider restart) may confirm the diagnosis, but a resource that cannot self-recover from a transient provider error is the actual defect.
  • Establish whether a bounded retry or a staleness check on LastAsyncOperation belongs in the repository-update contract, so a wedged resource is visible rather than silently frozen behind a stale message.

Acceptance criteria

  • All 20 Repository resources report ReconcileSuccess.
  • The mechanism that stopped the three from retrying is identified and recorded here.
  • A wedged resource is either recovered automatically or surfaced as a distinct signal, rather than presenting a stale message naming an already-fixed cause.

Blocks #112, which stays open until 20/20 is observed.

Evidence gathered live against admin@prod; no cluster mutation was performed, deliberately, so the stuck state remains available for diagnosis.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status
    📥 Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions