You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
An upstream that sends Go's zero time as expires_at ("0001-01-01T00:00:00Z") is never cached: every action is left pointing at upstream #142
BatchRewriter.ActionExpiry (GitLfsCache/Batch/BatchRewriter.cs, ~L229-254) reads any parseable expires_at as a real deadline. Go LFS servers that declare ExpiresAt time.Time with json:",omitempty" still emit the zero value, because omitempty has no effect on a struct type, so the action arrives as:
The git-lfs client treats a zero expires_at as "no expiry". Its expiry check (tools.IsExpiredAtOrIn) skips a zero time. The proxy reads it as an expiry that has already passed:
System.Text.Json parses the value as DateTimeOffset.MinValue.
LessMargin sees remaining <= 0 and returns MinValue unchanged.
The if (actionExpiresInSeconds <= 0) return; guard added in 246781e leaves the href and header pointing at upstream.
Why it matters
Behind such an upstream, every download and upload goes straight to the origin and nothing is ever cached. Nothing reports an error, and the only sign is that the cache hit metrics never move. Before 246781e the same input produced tokens that had already expired, so every transfer was refused with a 403. The recent fix changed the symptom but did not fix the cause.
Suggested fix
In ActionExpiry, treat an expires_at equal to DateTimeOffset.MinValue (the zero time) as absent, which matches git-lfs.
Optional, for a maintainer to decide: git-lfs also reads expires_in: 0 as "no expiry". Treating it as absent would contradict the existing test Rewrite_UpstreamHasNoWholeSecondLeft_LeavesTheActionPointingAtUpstream, so that is a deliberate choice.
Acceptance criteria
A batch response whose action carries "expires_at": "0001-01-01T00:00:00Z" gets its href rewritten to the proxy, its header removed, and expires_in == TokenLifetime.
Real past and near-future expires_at values behave exactly as they do today.
Priority: High. Behind any Go-based LFS upstream that emits the zero expires_at, the proxy silently stops caching anything. Every transfer goes straight to origin, nothing errors, and only flat cache-hit metrics show it. Data stays correct, but the cache's whole purpose fails without any sign. The fix is small and local.
Area / suggested assignment: Batch rewriting: BatchRewriter.ActionExpiry (GitLfsCache/Batch/BatchRewriter.cs, ~L229-254). Treat DateTimeOffset.MinValue as absent before LessMargin/Earliest, and add a rewrite test for "0001-01-01T00:00:00Z". Whether expires_in: 0 should also mean "no expiry" is a separate maintainer call, because it contradicts Rewrite_UpstreamHasNoWholeSecondLeft_LeavesTheActionPointingAtUpstream.
Duplicates / related: No duplicate found. This is a follow-up to 246781e, which turned the earlier 403 symptom into silent pass-through.
Activity
matt-edmondson commentedon Oct 30, 2026
Triage
expires_at, the proxy silently stops caching anything. Every transfer goes straight to origin, nothing errors, and only flat cache-hit metrics show it. Data stays correct, but the cache's whole purpose fails without any sign. The fix is small and local.BatchRewriter.ActionExpiry(GitLfsCache/Batch/BatchRewriter.cs, ~L229-254). TreatDateTimeOffset.MinValueas absent beforeLessMargin/Earliest, and add a rewrite test for"0001-01-01T00:00:00Z". Whetherexpires_in: 0should also mean "no expiry" is a separate maintainer call, because it contradictsRewrite_UpstreamHasNoWholeSecondLeft_LeavesTheActionPointingAtUpstream.Generated by Claude Code