The setting only production had
Open a game on gamekal.com while signed in, refresh a few times, and sometimes the page skips a whole section. The title and buttons are there, then Calendar Details, and nothing between them: no screenshots, no gallery frame, no gap where one should be. Refresh again and it’s back. Signed out, the same race shows “We could not find this game.”
It’s fixed, in the same release as the rest of that week’s work. The cause was a race between loading a game and loading one account setting, Filter Adult Games, that only my real account had switched off. Before the fix, 2 of 2 reloads with that setting off lost the gallery. After it, 2 of 2 kept it.
This post covers how a setting can throw away data that already arrived, why every test I had passed anyway, and the moment I had the bug on screen and explained it away. It also covers a pattern I kept running into that week: old decisions with buried side effects.
What the page does on load
A game page needs the game’s full record: description, release dates and the screenshots for its gallery. Cards elsewhere in the app carry a short version without screenshots. When a page has only a card, it asks for the full record.
The request carries the adult-content filter. Whether adult games are included depends on the account’s Filter Adult Games setting. Before your library loads, the app doesn’t know your setting yet, so it starts from a cached value or false, then reads the real one when the library arrives.
For most accounts the starting guess is right and nothing changes. For an account with the filter off, the value flips from false to true partway through the page load.
The record that arrived and was thrown away
The code that asks for a game remembered the setting it sent with the request. When the answer came back, it compared that with the current setting. If they differed, it threw the answer away, on the reasoning that it might include or exclude the wrong games.
That part was defensible. The next line wasn’t. After throwing the record away, it reported the lookup as answered. The page saw “answered”, marked the game as settled, and never asked again.
How the gallery disappeared
Signed in, Filter Adult Games off. Not to scale.
thrown away
Signed in, the page fell back to your shelf’s card for the game. Cards don’t have screenshots, and a gallery with nothing to show drew nothing. Signed out, there was no card to fall back on, so the page said the game didn’t exist.
The phone game sheet had the same gap, in the app and in the phone browser, because it uses the same lookup.
Why nothing caught it
Every number I’d checked that week said the page was fine. Here’s why each one missed it.
It needs a non-default setting. Every clean browser I tested with had the default filter, so the setting never flipped and the throwaway never ran. My own account, the one I use every day, was the only place it could happen.
It’s a race. The record has to arrive after the library. Locally, the record comes back almost instantly and usually wins. On the live site, over a real network with a real library, it often lost.
The tests checked pieces, not the sequence. Hundreds of unit tests passed, and none of them changed the setting while a request was in flight.
My layout check couldn’t see it. I’d spent the week driving layout shift to zero. A gallery that never appears doesn’t move, so it scores a perfect 0.
The moment I explained it away
Earlier that evening I had this bug on screen. My side-by-side loading check showed the loaded game page 593 px shorter than its loading skeleton: the frame went from 1,612 px to 1,019 px, and the gallery was missing. In a clean browser, it couldn’t be reproduced.
The explanation offered was reasonable: maybe my browser was blocking the screenshot images. I accepted it without running the one check that would have confirmed or ruled it out, the Network tab. Then I built a fix for that cause instead. It’s a good fix, since the gallery now keeps its frame and shows the cover if every screenshot fails to load, but it wasn’t this bug.
When something only happens on your machine, the difference between machines is the clue. Mine was signed in, with a real account, with a setting nobody else had.
Less than an hour later I refreshed a game page a few times, signed in, and the gallery vanished again. This time the cause got traced instead of explained.
The fix
Ask again instead of reporting “answered”. If the setting changed while the request was out, the lookup now asks once more with the current setting. If it changes again during that second request, the answer is “unanswered”, and the page’s own retry asks later. It can’t loop, and it never reports a dropped record as an answer. The decision lives in one small function with tests for every combination: kept, asked again, or left unanswered, never dropped.
Hold the gallery while a card is showing. On the desktop page, while the page shows a card and the full record hasn’t arrived, the gallery draws its loading skeleton at its final size. When the screenshots arrive, they swap in without moving the page. The phone sheet already did this.
A card never replaces a full record. That was already true. There’s now a test that pins it in both orders.
Reloads that kept the gallery
Game page, Filter Adult Games off, local reproduction. Higher is better.
Two reloads isn’t a rate. It’s a reproduction. The real check was the one that found it: my account, the live site, refreshing until I was sure.
Old decisions, buried side effects
This wasn’t the only bug that week that came from a decision that made sense when it was made.
- The calendar held 9,612 months so paging would never run out. On iPhone Safari, that list scrolled itself back toward the year 1600 (The calendar that scrolled itself back to 1600).
- Sign-in links were prepared on page load so DuckDuckGo on a phone wouldn’t drop the redirect. That left two secrets fighting over one storage slot (A sign-in that only looked like it worked).
- A gesture library’s default of
touch-action: nonestopped phone-web pages from scrolling when a swipe started on a slider. - A filter setting threw away a correct record to avoid showing the wrong one.
Each was a reasonable answer to the problem in front of it. Each had a side effect in a place nobody was looking: a different browser, a different provider, a different account. None of them would show up in a clean test environment.
What I learned
Looking back, four lessons stand out.
Test with a real account, not only a clean one. Clean profiles are good at proving the default path works. Your users, and you, live on the non-default paths: settings changed, years of data, a library that takes a moment to load.
A dropped result must never look like an answer. “I threw it away” and “I got it” need different return values. Merging them turned a harmless retry into a permanent gap.
Confirm the cause before you fix it. I had a 593 px measurement and a plausible story. A one-minute Network check would have told me the story was wrong.
Absence doesn’t shift. A layout metric can’t catch something that never renders. If a section can be missing, check that it’s there, not only that it holds still.
What’s next?
A check that loads the main pages signed in with a seeded, non-default account, its adult filter and other settings flipped from the defaults, and asserts that each section actually rendered, not only that nothing moved.
The gallery bug lived in my account the whole time. It took using the app the way I actually use it to find it.