Engineering
A bug that shouldn't have been possible
Every fact we checked said this request should succeed. It didn't. Here's what was actually happening.
The report
A real user reported a simple error: creating a project inside our Young Creators feature failed with "This curriculum module does not exist." Straightforward enough — except the module clearly did exist. It was visibly selected on screen, its real instructions were showing correctly, and it had already worked earlier in the same session.
First theory: a missing migration
The obvious first guess was that a recent schema change hadn't actually been applied to the live database yet. We asked for a direct query against the real table. It came back with all fifteen expected rows, correctly populated. Not that.
Second theory: the wrong row
Next we asked for the exact row by its real ID — the specific one the error referred to. It came back, active, correctly matching the module the user had actually selected. Not that either.
Third theory: stale code
Maybe the deployed backend was an older version, one where this specific check had a real, since-fixed bug. We had the user paste the actual, complete deployed file. Read through it directly. The logic was correct, and matched the database perfectly.
Fourth theory: the frontend was lying about what it sent
This is usually where a mismatch like this actually lives — a stale form value, a variable holding the wrong ID after some earlier action. We added a temporary line of code that put the exact, real value being submitted directly into the visible error message, so there'd be no more guessing.
The ID it showed matched the confirmed, correct, active row — exactly, character for character.
At this point, every real, checkable fact agreed the request should succeed. The database had the row. The code was correct. The frontend was sending the right value. And it was still failing.
The actual cause
The real answer had nothing to do with any of our own application code. A table shared across every account — reference content everyone should see identically — had Row Level Security switched on, with zero real policies ever defined for it.
Postgres's own real, correct, default behavior for exactly that combination is to silently deny access to any role that isn't explicitly exempt from the check — regardless of whether the row genuinely exists, or whether the requesting query is otherwise flawless. Our own database administrator connection, the one used to check the data directly, bypasses that restriction entirely — which is exactly why every direct query we ran kept confirming the row was fine. The live application, running under a real, more restricted connection, was silently blocked from ever seeing it at all.
Once we checked, this same misconfiguration turned out to affect thirty-six real tables across the database — almost certainly switched on by a hosting dashboard's own security suggestion at some point, entirely independent of anything in our own migration history.
Why we're writing this down
The honest lesson here isn't really about Row Level Security specifically. It's that a bug can be completely real and completely invisible to every layer you'd normally check individually — the schema, the deployed code, the actual request — while the actual cause sits in a completely different place none of those checks would ever reach. The only way we found it was refusing to stop once every individual piece had been separately confirmed correct, and asking a genuinely different kind of question instead of re-checking the same three things again.