fix: treat an empty Raider.IO customization as absent - #35
Merged
Conversation
Raider.IO sends discord_profile: "" for a player who never set one. The schema required a non-empty string, so the parse threw, the client raised schema_drift, and the run failed with non-retryable upstream_schema_changed — "The search could not be completed" for every such character, permanently. An empty value is an absent value, not structural change to the payload. Accept it and normalize it to no profile guess, so nothing downstream searches on an empty alias. Verified against the live eu/draenor/shurkle payload that produced the failure in the test environment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qh8Zb2HnaxebWrRLUMoAiv
Erilla
added a commit
that referenced
this pull request
Aug 10, 2026
Follows #35. With the parse fixed, `eu/draenor/shurkle` got further and then failed five attempts in ~130 ms each as `upstream_unavailable`, with zero Blizzard requests — "Character data is temporarily unavailable." The cause, live: ``` GET /api/user/view-characters?name=shurkle 403 {"error":"Forbidden","message":"The requested user's profile is private and cannot be viewed.","errorCode":"profile_is_private"} ``` `shurkle` has no public Raider.IO ownership, so discovery falls back to profile guesses — one of which is the character's own name. That name belongs to a user whose profile is private. `responseFailure` mapped every non-404 to `transient`, so a permanent answer about visibility was retried as an outage and then failed the whole run. - 403 is now a distinct `forbidden` failure kind. - `resolveProfileGuess` treats `forbidden` like `not_found` and returns `null`: an invisible profile yields no relationships. A guess is speculative by nature, so failing the run on one is wrong regardless. - `getCharacter` and `getClaimedCharacters` keep their current behaviour for a 403 (retryable), since neither has a confirmed live case. Worth a look separately — a 403 there is equally permanent. New fixture with the real body, plus a test that was red with `raiderio_transient`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Qh8Zb2HnaxebWrRLUMoAiv Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 10, 2026
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.
eu/draenor/shurklefailed intestwithupstream_schema_changedin 363 ms, before the fingerprint stage ran. Running the real parser against the live payload gives:Raider.IO sends
discord_profile: ""for a player who never set one.z.string().min(1)rejected it, the client raisedschema_drift, and the handler mapped that to non-retryableupstream_schema_changed— so the search failed permanently, showing "The search could not be completed." for every character with an empty Discord field.An empty value is an absent value, not structural change to the payload, which is what
schema_driftis reserved for (normalize.tssays so in its own comment). It is now accepted and normalized toprofileGuess: null, so nothing downstream searches on an empty alias.Pre-existing since the MVP, not introduced by the fingerprint work.
New fixture plus a test, red with
raiderio_schema_driftbefore the change, and the liveshurklepayload now parses.Worth noting the same class of risk remains for other
min(1)upstream fields (user.name,main_character.name,main_character.path). I fixed only the field with a confirmed live failure rather than loosening the schema wholesale — but any of them arriving empty would fail a search the same permanent way.🤖 Generated with Claude Code
https://claude.ai/code/session_01Qh8Zb2HnaxebWrRLUMoAiv