Skip to main content

When a build wedges

kpack does NOT auto-retry once an Image's latest Build is in Succeeded=False. The Image stays wedged on the failed build — even if you push new code, even if the underlying cause is fixed externally, even if you set the kpack.io/rebuild annotation. The only thing that unwedges kpack is deleting the failed Build resource.

BuildRepairAgent (15-minute cadence) detects every wedged Image cluster-wide and emits OTel alerts to the /agents panel on the web console and the Alerts tab on iOS. Each alert tells you exactly what to run.

The four patterns​

PatternWhat you'll see in the alertWhat it meansFix
missing-saserviceaccounts "system-default" not foundNew project namespace skipped the kpack ServiceAccount bootstrapApply the SA yaml + kubectl delete build.kpack.io ...
git-auth-dead-patInvalid username or token. Password authentication is not supportedYour git credential PAT expired or was revokedc2a creds rotate ... --from-gh then kubectl delete build.kpack.io ...
git-auth-otherauthentication required / Repository not foundSA missing the per-app git secret, or wrong credsPatch the SA's secrets[], then delete the failed Build
transient-or-unknownOOM / network glitch / otherProbably a one-offDiagnose with kubectl describe build.kpack.io ..., then delete the failed Build

Every fix ends with kubectl delete build.kpack.io <namespace>/<latest-build-name> because that's the missing step.

Why c2a app rebuild alone isn't enough in the wedged state​

The rebuild API sets the kpack.io/rebuild annotation. In the non-wedged state (latest Build succeeded), kpack creates a fresh Build in response. In the wedged state (latest Build is Succeeded=False), kpack sees the annotation, sets its internal additionalBuildNeeded status, but does NOT schedule a new Build.

This is a footgun the CLI used to swallow — it returned ✅ Rebuild triggered successfully! even when no Build was scheduled. After clue2app-cli#67, the CLI verifies by default:

c2a app rebuild my-app
# Triggering rebuild for: my-app
# Verifying kpack actually scheduled a new Build (polling up to 30s)…
# ✅ New build scheduled: my-app-image-build-7 (3.2s ago)

If the verification finds no new Build, the CLI prints the unstick recipe instead of pretending success:

⚠️ Rebuild API returned success but kpack did NOT schedule a new Build.
This is the wedged-Image state...

Unstick recipe:
1. Inspect the failure: kubectl describe build.kpack.io <ns>/<latest>
2. Fix the underlying cause (SA missing, git-secret unbound, dead PAT — see `c2a creds check`)
3. Delete the failed Build to let kpack reschedule: kubectl delete build.kpack.io <ns>/<latest>

--no-verify opts out (for CI fire-and-forget); --verify-timeout=N adjusts the poll budget.

The "kpack reuses the build name after delete" gotcha​

When you delete image-build-4, kpack might create a new image-build-4 (same number) rather than image-build-5. The image's status.buildCounter doesn't advance on a manual delete.

So if your watcher script looks for "a build with a higher number" as proof your fix worked, it'll never see it. Compare metadata.creationTimestamp instead, not the number.

Where this fits in the shared responsibility model​

  • The platform (automated) detects wedged builds and surfaces the recipe. v2 of BuildRepairAgent (rolling out next, behind BUILD_REPAIR_ACTIVE=true) will apply the recipe itself for the known-safe patterns (missing-sa, git-auth-other) after a one-week dry-run validation period.
  • You apply the recipe today, or wait for v2.
  • The platform (manual) rotates platform-owned credentials when those are the root cause (e.g. the shared control-services-git-creds PAT).

See the Shared Responsibility Model for the broader split.

  • Pinning apps warm — separate verb, doesn't trigger any reconciliation that could wedge a build
  • Daari Secrets — secret rotation here doesn't touch builds; rotation propagation is via ksvc restart