Hive Hive
Sign in

fix(cli): refresh Bazel credential-helper token before the expiry boundary

GitHub issue · Closed

Metadata
Source
tuist/tuist #11341
Updated
Jun 24, 2026
Domains
CLI
Details

What changed

The tuist bazel credential-helper now refreshes a user’s access token proactively when it is within a 60s safety margin of expiring, and reports expires to Bazel brought forward by that same margin (exp − 60s). Project tokens (no expiry) and account tokens (cannot be refreshed) are reported unchanged.

Why

Bazel remote-cache builds against Kura burst with UNAUTHENTICATED a few minutes into a build — on whichever operation dominates at that instant — and then recover. In two captured gRPC logs:

  • bazel-grpc-4: 104× UNAUTHENTICATED on Write in a 2.0s burst at +348s
  • bazel-grpc-6: 1089× UNAUTHENTICATED on Read in a 0.6s burst at +414s

Both bursts are tight and land on the access token’s 10-minute (600s) TTL boundary (the token was already partially aged at build start).

Root cause

Bazel’s CredentialCacheExpiry caches the credential-helper response until exactly the expires we report and only re-invokes the helper lazily, on the first request issued after that timestamp — it never refreshes early (the --credential_helper_cache_duration flag is only the no-expires fallback). The helper reported the access token’s exact exp, leaving zero margin, so Bazel kept attaching the token to requests right up to the boundary. The in-flight batch of remote-cache RPCs around the boundary then reached Kura after exp and was rejected.

Kura validates the Tuist JWT statelessly (signature + exp, locally, via kura.jwt_verify), so issuing a new access token via refresh never revokes a previously issued one — the only thing that produces UNAUTHENTICATED is exp passing. This rules out the alternative theory (parallel credential-helper calls minting tokens that invalidate each other): stateless JWTs are not revoked on refresh.

Why this solution

The boundary failure is fundamentally that Bazel reuses a cached credential up to the instant it expires. Reporting an earlier expires makes Bazel reload the helper ~60s before the real expiry; pairing that with a proactive refresh at the same threshold means the helper hands back a token with a full lifetime ahead of it. The result: no in-flight request can ever carry a token within 60s of being rejected, which comfortably covers request latency and clock skew between a developer machine and the cache. The 60s margin is well under the 600s TTL, so it costs roughly one extra refresh per ~9 minutes of continuous building.

The refresh is best-effort: if it fails (e.g. a transient network error) the helper falls back to the token it already has, which is still valid for up to the safety margin, rather than failing the request outright.

User impact

Developers running long Bazel builds against the Tuist/Kura remote cache no longer hit a mid-build wave of UNAUTHENTICATED errors (and the costly stall/retry that followed) when their short-lived access token crosses its expiry.

Validation

  • Unit tests cover the margin reporting, the proactive refresh within the margin, and the best-effort fallback when the refresh fails (plus existing project/account/error paths). All pass; swiftformat + swiftlint clean.
  • The built binary against staging returns a freshly refreshed token and reports expires exactly 60s before the JWT exp.
  • A real build issuing 2400+ authenticated gRPC requests across a token reported-expiry boundary completed with zero UNAUTHENTICATED.

🤖 Generated with Claude Code

Comments
T
tuist[bot] Jun 18, 2026

🛠️ Tuist Run Report 🛠️

Tests 🧪
Scheme Status Cache hit rate Tests Skipped Ran Commit
TuistUnitTests 99 % 28 39 -11 fb87b13e3
Builds 🔨
Scheme Status Duration Commit
TuistUnitTests 22.4s fb87b13e3
TA
tuist-atlas[bot] Jun 19, 2026

The fix for refreshing the Bazel credential-helper token before the expiry boundary is now available in 4.201.0-canary.9. The helper now proactively refreshes access tokens when within a 60 second safety margin of expiring, preventing mid-build UNAUTHENTICATED errors during long Bazel builds against remote caches. Update to 4.201.0-canary.9 to get this fix.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for refreshing the Bazel credential-helper token before the expiry boundary is now available in version 4.201.0-canary.13. Update to that version to prevent mid-build UNAUTHENTICATED errors with Bazel remote caching.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for refreshing the Bazel credential-helper token before expiry is now available in 4.201.0-canary.12. The credential helper now proactively refreshes access tokens when within a 60-second safety margin of expiration, preventing mid-build waves of UNAUTHENTICATED errors during long Bazel builds. Update to this version to get the fix.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for refreshing Bazel credential-helper tokens before the expiry boundary is now available in 4.201.0-canary.11. The token is now proactively refreshed when within a 60s safety margin of expiring, preventing mid-build UNAUTHENTICATED errors during long Bazel builds against the Tuist remote cache.

Update to 4.201.0-canary.11 to use this fix.

TA
tuist-atlas[bot] Jun 19, 2026

This is now available in 4.201.0-canary.10. Update to pick up the fix that refreshes the Bazel credential-helper token before the expiry boundary.

TA
tuist-atlas[bot] Jun 20, 2026

The fix to refresh the Bazel credential-helper token before the expiry boundary is now available in 4.201.0-canary.18. Update to this version to get it.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for refreshing Bazel credential-helper tokens before expiry is now available in 4.201.0-canary.17. Update to this version to prevent mid-build authentication failures.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for refreshing the Bazel credential-helper token before the expiry boundary is now available in 4.201.0-canary.16. The helper now proactively refreshes tokens 60 seconds before expiration to prevent mid-build UNAUTHENTICATED errors. Update to this version.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for refreshing the Bazel credential-helper token before the expiry boundary is now available in 4.201.0-canary.15. The helper now refreshes access tokens proactively when within a 60s safety margin of expiring. Update to this version to use the fix.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for refreshing Bazel credential-helper tokens before the expiry boundary is now available in version 4.201.0-canary.14. Update to this version to prevent mid-build UNAUTHENTICATED errors during long Bazel remote-cache builds.

TA
tuist-atlas[bot] Jun 21, 2026

Bazel credential-helper token refresh before the expiry boundary is now available in 4.201.0-canary.19. This proactively refreshes access tokens within a 60-second safety margin to prevent mid-build UNAUTHENTICATED errors. Update to 4.201.0-canary.19 to get this fix.