Stephen
Hackett-Delaney
Full Stack Software Engineer
The Subtle Art of Clearing Caches
February 16, 2026
We had a stale auth session stuck in the iOS simulator. The app was trying to use a cached session from a previous login, so it wouldn't let us log in fresh. The fix seemed obvious: delete AsyncStorage. So we did.
rm -rf ".../RCTAsyncLocalStorage_V1"The stale session was gone. The app loaded clean. We logged in with fresh credentials. And then:
ERROR signInWithPassword threw: [Error: Failed to write value.
Error Domain=NSCocoaErrorDomain Code=4
"The folder 'e6e9721590ffc7d457e55c3e577989d2' doesn't exist."]The login succeeded on the server — Supabase returned a valid session. But the Supabase client couldn't persist that session because AsyncStorage tried to write to a directory that no longer existed. We'd fixed the stale session by destroying the storage layer entirely.
How Did We Get Here?
We were testing the brand onboarding flow in Maura, a React Native app. The development cycle looked like:
- Run database reset script (clears brand data, regenerates invite tokens)
- Activate via web (set new password)
- Log in on the iOS simulator
- Test the onboarding flow
- Repeat
The problem: step 3 stores a Supabase auth session in AsyncStorage. When we ran the reset script and repeated the cycle, the old session was still cached. The app would try to use it instead of prompting a fresh login.
The quick fix everyone reaches for: clear the cache. In React Native on iOS, that means deleting the RCTAsyncLocalStorage_V1 directory from the simulator's app container.
We'd done this before and it worked. But this time, we used rm -rf on the directory itself — not just its contents. AsyncStorage expects the directory to exist. When it doesn't, the native module throws a write error.
The Subtle Difference
# What we did (broke things):
rm -rf ".../RCTAsyncLocalStorage_V1"
# What we should have done (safe):
rm -rf ".../RCTAsyncLocalStorage_V1/*"One character difference. One trailing /*. The first deletes the container. The second deletes the contents.
AsyncStorage on iOS uses a folder of JSON files, hashed by key name. The RCTAsyncLocalStorage_V1 directory is the root. When you delete it, the native module doesn't recreate it — it just fails on the next write.
The fix was trivial once we understood it:
mkdir -p ".../RCTAsyncLocalStorage_V1"Recreating the directory let AsyncStorage write again, and the login worked immediately.
The Process Shortcomings
We treated cache clearing as a safe operation. It's not. Deleting cached data is safe. Deleting the infrastructure that holds cached data is destructive. We didn't distinguish between the two.
We used rm -rf without checking what we were deleting. The command was pasted from a previous session where it happened to work (because the directory was recreated by a reinstall). This time the context was different — no reinstall — but we ran the same command.
We had no recovery plan. When the login broke after cache clearing, it took significant debugging to trace it back to the missing directory. The error message ("The folder doesn't exist") was clear in hindsight, but it appeared as an AsyncStorage write error inside the Supabase auth flow — not obviously related to the directory we'd deleted minutes earlier.
The Debugging Trail
What made this hard to diagnose was that the error presented as an auth problem:
- First symptom: Login form submits but the app doesn't navigate.
- Added logging:
signInWithPasswordwasn't returning — it was throwing. - The error: AsyncStorage
multiSetfailed because the storage directory didn't exist. - Root cause: We'd deleted the directory, not its contents.
The error was four layers deep: UI (no navigation) → Auth (no session) → Storage (write failed) → Filesystem (directory missing). Each layer suggested a different problem. Only the deepest layer told the truth.
The Rule
When clearing caches, delete the contents, not the container.
This applies beyond AsyncStorage:
| Storage | Container | Contents |
| AsyncStorage | RCTAsyncLocalStorage_V1/ | *.json files inside |
| iOS Keychain | Keychain database | Individual keychain items |
| Browser localStorage | The storage API | Individual keys |
| Redis | The Redis instance | Individual keys (FLUSHDB) |
| Docker volumes | The volume mount | Files inside the volume |
In every case, the system expects the container to exist. Delete the contents, keep the structure.
What I Learned
"Clear the cache" is not one operation. It's at least two: identifying what to clear, and clearing it without breaking the storage mechanism. The second part is where mistakes happen.
Quick fixes need the same scrutiny as code changes. We'd never write DROP TABLE without thinking. But rm -rf on a storage directory felt safe because it was "just cache." Storage infrastructure is not cache.
When debugging, add logging at every layer. We found this because we wrapped signInWithPassword in a try/catch that printed the actual error. Without that, we'd have been debugging auth state indefinitely while the real problem was a missing directory on disk.
This is Part 3 of the Debugging series. Next up: iOS Simulator Gotchas — the persistence model that catches every React Native developer off guard.
© 2026. All rights reserved