| name | stale-cache-external-service-recovery |
| description | Handle stale local cache when external service loses/resets data. Use when:
(1) Token refresh fails with "not found" or 404 errors, (2) Cached identifiers
(pubkeys, user IDs) don't exist on external service anymore, (3) External service
was reset/migrated but local cache has old values. Pattern: detect 404 specifically,
delete stale cache entry, recreate on external service, update cache with new values.
Complements local-cache-idempotency-fallback skill.
|
| author | Claude Code |
| version | 1.0.0 |
| date | "2026-01-25T00:00:00.000Z" |
Stale Cache Recovery When External Service Loses Data
Problem
When using your database as a cache/idempotency layer (see local-cache-idempotency-fallback),
a new failure mode emerges: the external service loses or resets its data, but your
cache still has the old values. Operations fail with misleading errors because the cached
identifiers don't exist on the external service anymore.
Context / Trigger Conditions
- Token refresh fails with 404 "User with pubkey/id not found"
- Operations fail with "Invalid or expired token" but the real issue is missing user
- External service was reset, migrated, or lost data
- Cached identifiers (pubkeys, API keys, user IDs) are valid in cache but not on service
- Error message is misleading - suggests token issue when it's actually a missing resource
Solution
Detect the specific "not found" case and handle it separately from other errors:
if (cached) {
pubkey = cached.pubkey;
token = cached.token;
try {
const freshToken = await externalApi.getTokenByPubkey(pubkey);
token = freshToken;
console.log(`Pubkey: ${pubkey} (token refreshed)`);
} catch (error) {
const errMsg = error instanceof Error ? error.message : String(error);
if (errMsg.includes("not found") || response.status === 404) {
console.log(`Cached identifier not on external service, recreating...`);
await db.deleteUser(userId);
const result = await externalApi.createUser(userId, username);
pubkey = result.pubkey;
token = result.token;
console.log(`Pubkey: ${pubkey} (recreated)`);
await db.saveUser({ userId, pubkey, token });
} else {
.();
;
}
}
}
Key Pattern
- Attempt normal refresh first: Try to validate/refresh cached credentials
- Detect 404 specifically: Check for "not found" in error message or 404 status
- Delete stale entry: Remove the invalid cache entry before recreating
- Recreate on external: Create fresh resource with same input identifiers
- Update cache: Store new values for future runs
- Distinguish from other errors: Only handle 404; other errors may need different handling
Verification
- Simulate external service data loss (or clear its database)
- Run script with stale local cache
- Should see "recreating" message, not crash with 401/404
- New values cached and used for subsequent operations
- Script completes successfully
Example
Real-world application - Keycast pubkey cache recovery:
const cached = await pgDb.getImportedUser(creator.user_id);
let pubkey: string;
let token: string;
if (cached) {
pubkey = cached.pubkey;
token = cached.token;
try {
const freshToken = await keycast.getTokenByPubkey(pubkey);
token = freshToken;
await pgDb.saveImportedUser({ ...cached, token: freshToken });
console.log(`Pubkey: ${pubkey} (token refreshed)`);
} catch (refreshError) {
const errMsg = refreshError instanceof Error ? refreshError.message : String(refreshError);
if (errMsg.includes("not found")) {
console.log(`Cached pubkey not on Keycast, recreating user...`);
await pgDb.deleteImportedUser(creator.user_id);
const result = await keycast.createPreloadedUser(
creator.user_id,
username,
displayName
);
pubkey = result.;
token = result.;
.();
pgDb.({
: creator.,
: creator.,
pubkey,
token,
: ,
});
} {
.();
.();
;
}
}
}
Notes
- This complements
local-cache-idempotency-fallback - use both together
- The misleading error chain: 404 "not found" → continue with stale token → 401 "invalid token"
- Always delete before recreate to avoid accumulating orphan cache entries
- Consider logging which entries were stale for debugging/monitoring
- If external service is frequently losing data, investigate root cause
- The new pubkey will be different from the old one - downstream systems may need updates
Related Skills
local-cache-idempotency-fallback: The base pattern this skill extends
- Database retry logic for connection resets (separate concern)