Stop a 404 or a rate limit being read as the offsets data - #414
Merged
Conversation
Both fetchers handed the response straight to response.json() without looking at the status, so a non-200 whose body happens to be JSON was returned as if it were the lookup or the offsets. Whether that threw depended on what the host chose to return: raw.githubusercontent.com answers a missing file with plain text, which throws, but a rate limit or an error page carrying JSON is accepted. There was also only one attempt per host and no timeout. raw.githubusercontent.com rate limits per IP, so a household where several people start the game at once, or anyone behind a shared address, could be turned away on both hosts and told to check their internet connection; and a request that hung held up the start with nothing to cut it off. The two fetchers were the same function with a different path and error, so this folds them into one that checks the status, tries both hosts, tries again twice after a backoff, and times out after ten seconds. The thrown value is unchanged, so the caller that falls back to the cached lookup still sees what it expects. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Lucas Greuloch (greluc) <lucas.greuloch@gmail.com>
|
Download the artifacts for this pull request:
This service is provided by nightly.link. These artifacts will expire in 90 days and will not be available for download after that time. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A 404 or a rate limit from the offsets mirror can be read as the offsets data.
Why
Both fetchers in
src/main/offsetStore.tshand the response straight toresponse.json():Nothing looks at the status, so a non-200 whose body happens to be JSON is returned as if it were the payload. Whether that throws depends on what the host chose to send back:
raw.githubusercontent.comanswers a missing file with the plain text404: Not Found, which fails to parse and is caught — but an error or rate-limit page carrying JSON parses cleanly and is accepted. The offsets decide where the client reads inside the game's memory, so what happens next is not a clean failure.Two smaller things sit alongside it:
raw.githubusercontent.comrate limits per IP. A household where several people start the game at once, or anyone behind a shared address, can be turned away by both hosts and told to check their internet connection.What changed
fetchOffsetLookupJsonandfetchOffsetsJsonwere the same function with a different path and a different error, so they are folded into one:It checks
response.ok, tries both hosts, tries again twice after a growing backoff, and gives each request ten seconds. The thrown value is unchanged — stillErrors.LOOKUP_FETCH_ERROR/Errors.OFFSETS_FETCH_ERROR— sofetchOffsetLookup's fallback to the cached lookup still sees exactly what it expects. Net: 39 insertions, 31 deletions, one file, no new dependencies.Verification
There is no test runner in this repository, so the check is a harness that extracts the real fetcher from
offsetStore.tsand drives it against a scriptedfetch. The same script runs against either revision, which is what makes the before column meaningful. The timeout and backoff constants are shrunk from seconds to milliseconds so it finishes; the control flow around them is untouched.{"error":"Not Found"}as the lookup{"message":"rate limited"}as the lookupLOOKUP_FETCH_ERROReslint srcandtsc --noEmitare clean, andprettierreports the file unchanged.Limits, stated plainly
The harness drives the fetcher with a fake
fetch; nothing here was run against the real mirror or a real rate limit, and the app was not started. The retry budget is a judgement call — three rounds over two hosts, backing off 1.5s then 3s, so a completely dead mirror now costs about 4.5 seconds before falling back to the cache rather than failing immediately. If you would rather that be shorter, or the timeout longer, say so and I will change it; the constants are together at the top.I have not touched the caching or validation of what comes back, which is a separate question from whether the fetch itself is sound.
Disclaimer
This change was written with AI assistance (Claude Code). The author reviewed it, and every claim above was checked by running code rather than reasoned about — the before/after table is real output from the harness against both revisions. Please review it as you would any patch from a stranger; if something here is wrong I would rather hear it than have it merged.
🤖 Generated with Claude Code