Guard /internal/refresh per game so a feed escape cannot 500 it - #129
Merged
Conversation
…0 it Twice now a feed has thrown a type RefreshGame's catch filter did not match - XmlException from MegaMillions, then plain ArgumentException from Draw.Create - and both times this endpoint answered 500. It is what the keep-alive workflow calls, so a 500 reads as the whole instance being down. Each of those was fixed at its source, but the endpoint itself had no guard, so the next escape would do the same thing. This closes the class rather than the instances. Per game, not around the loop: the unguarded loop aborted on the first throw, so every game after the failing one was silently skipped. The catch is not a swallow - it logs the exception with its game, and records a RefreshResult carrying the message as feedError, which is the same shape the response already uses for the failures RefreshGame does anticipate. Callers see a 200 with the error named per game. OperationCanceledException is deliberately excluded: a shutdown or a disconnected client is not a feed failure and must keep propagating. Two tests, both verified to fail without the guard: an unanticipated exception type is reported rather than 500ing, and one game failing does not cost the other its refresh. 321 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWkVh7cyAz1gWapBH1CY8n
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.
Twice now a feed has thrown a type
RefreshGame's catch filter did not match —XmlExceptionfrom MegaMillions (#117), then plainArgumentExceptionfromDraw.Create(#126) — and both times this endpoint answered 500. It is what the keep-alive workflow calls, so a 500 reads as the whole instance being down.Each was fixed at its source. The endpoint itself still had no guard, so the next escape would do exactly the same thing. This closes the class rather than the instances.
Per game, not around the loop
The unguarded loop aborted on the first throw, so every game after the failing one was silently skipped — a second, quieter bug in the same code.
The catch is not a swallow
It logs the exception with its game, and records a
RefreshResultcarrying the message asfeedError— the same shape the response already uses for the failuresRefreshGamedoes anticipate. Callers get a 200 with the error named per game, which is the existing contract, not a new one.OperationCanceledExceptionis deliberately excluded: a shutdown or a disconnected client is not a feed failure and must keep propagating.Tests
Two, both verified to fail without the guard before being accepted:
AnUnanticipatedFeedFailure_IsReported_NotA500— a feed throwingNotSupportedException(a type nothing in the chain anticipates) yields 200 with the error reported.OneGameFailing_DoesNotCostTheOtherItsRefresh— both games still appear, and the healthy one has nofeedError.321 tests pass,
dotnet build -warnaserrorwith zero warnings.🤖 Generated with Claude Code