Skip to content

Fix infinite loop on IsValidBase - #20

Merged
hahn-kev merged 3 commits into
claude/csharp-rewritefrom
claude/csharp-isvalidbase-empty-repo
Aug 4, 2026
Merged

Fix infinite loop on IsValidBase#20
hahn-kev merged 3 commits into
claude/csharp-rewritefrom
claude/csharp-isvalidbase-empty-repo

Conversation

@hahn-kev

@hahn-kev hahn-kev commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

this caused a crash in production recently, we're fixing it.


🤖 AI summary

On an empty repo (hg init, zero changesets), IsValidBase hung the pull request in an infinite loop. GetRevisionsInternal handles an empty repo with a special case that returns the "0:" sentinel — but the check ran before Skip(offset).Take(quantity), so it returned that same sentinel for every offset. IsValidBase then paged forever: any baseHash other than "0" never matched, the page was never empty, and the offset grew without bound.

Fix — two complementary parts:

  1. Root cause (GetRevisionsInternal): only emit the "0:" sentinel at offset 0; paginating past it returns an empty list, so callers see a terminating page. The offset-0 contract (getRevisions, GetTip, GetBranchTips) is unchanged.
  2. Defensive guard (IsValidBase): a page shorter than the requested quantity is the last page, so stop rather than advancing the offset again. This guarantees termination even against any future GetRevisions quirk that returns a fixed non-empty page regardless of offset.

Either part alone stops the hang; together they fix the cause and make the loop structurally incapable of running forever.

Also adds an HTTP-level regression test (PullBundleChunk_EmptyRepoWithNonZeroBaseHash_FailsWithoutHanging) that pulls an empty repo with a non-zero baseHash and asserts FAIL within a timeout instead of hanging.

Test plan

Verified against the rebuilt image (repos seeded before container start, to sidestep a local Docker-Desktop docker cp-into-running-container visibility quirk):

Case Before After
empty repo, baseHash=0 NOCHANGE NOCHANGE
empty repo, baseHash=fakehash hung (timeout) FAIL (~0.5s)
non-empty repo, valid hash NOCHANGE NOCHANGE
non-empty repo, bogus hash FAIL FAIL
getRevisions offset 0 on empty repo 0: sentinel 0: sentinel (unchanged)

The new xUnit test runs in CI (Linux); it can't seed repos through the fixture locally on Windows due to the docker cp overlay quirk noted above, which also affects the pre-existing empty-repo test.


This change is Reviewable

hahn-kev and others added 3 commits August 3, 2026 16:51
On an empty repo (hg init, zero changesets), GetRevisionsInternal hit its
empty-repo special case and returned the "0:" sentinel for every offset,
because the check ran before Skip(offset).Take(quantity). IsValidBase then
paged forever: any baseHash other than "0" never matched, the page was never
empty, and the offset grew without bound, hanging the pull request.

Fix the root cause (only emit the sentinel at offset 0; return empty past it)
and add a defensive guard in IsValidBase: a page shorter than the requested
quantity is the last page, so stop rather than advancing the offset again.
This guarantees the loop terminates even against any future GetRevisions quirk
that returns a fixed non-empty page regardless of offset.

Add an HTTP-level regression test that pulls an empty repo with a non-zero
baseHash and asserts it returns FAIL within a timeout instead of hanging.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a >200-revision fixture (manyRevsHgRepo, 205 commits) and a pull test that
requests a non-existent baseHash against it. This forces IsValidBase past its
first full page (offset 0 -> 200) onto a short final page, exercising the
offset-advancement and short-page-break branch that the single-page
InvalidHash test never reaches. Asserts FAIL within a timeout so a
non-terminating loop would surface as a fast failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hahn-kev
hahn-kev merged commit 33b755d into dockerize Aug 4, 2026
2 checks passed
@hahn-kev
hahn-kev deleted the claude/csharp-isvalidbase-empty-repo branch August 4, 2026 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant