Skip to content

Commit 312c91b

Browse files
committed
fix(webapp): don't cache getEntitlement fail-open fallback
Devin caught a correctness bug in the previous commit. Returning { hasAccess: true } from inside the SWR loader on error caused that fail-open value to be cached for 60-120s, which could overwrite a legitimate hasAccess: false during a transient billing outage and grant a blocked org access for up to 120s. Fix: catch errors inside the loader (so we don't trigger the @unkey/cache unhandled-rejection issue during background revalidation) and return undefined. Apply the fail-open default *outside* the SWR call so it never becomes a cached access decision. Trade-off: returning undefined from the loader still overwrites the previous cached entry with an undefined value, but @unkey/cache's swr() treats an undefined cached value as a miss and re-fetches on the next request — so on billing recovery, the cache picks up the real result immediately rather than serving a stale fail-open for up to 120s.
1 parent 33253dc commit 312c91b

1 file changed

Lines changed: 15 additions & 6 deletions

File tree

apps/webapp/app/services/platform.v3.server.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,24 +536,33 @@ export async function getEntitlement(
536536
): Promise<ReportUsageResult | undefined> {
537537
if (!client) return undefined;
538538

539+
// Errors must be caught inside the loader — @unkey/cache passes the loader
540+
// promise to waitUntil() with no .catch(), so an unhandled rejection during
541+
// background SWR revalidation would crash the process. Returning undefined
542+
// on error tells SWR not to commit a fail-open value to the cache, which
543+
// prevents transient billing errors from overwriting a legitimate
544+
// hasAccess: false entry. The fail-open default is applied *outside* the
545+
// SWR call so it never becomes a cached access decision.
539546
const result = await platformCache.entitlement.swr(organizationId, async () => {
540547
try {
541548
const response = await client.getEntitlement(organizationId);
542549
if (!response.success) {
543550
logger.error("Error getting entitlement - no success", { error: response.error });
544-
return {
545-
hasAccess: true as const,
546-
};
551+
return undefined;
547552
}
548553
return response;
549554
} catch (e) {
550555
logger.error("Error getting entitlement - caught error", { error: e });
551-
return {
552-
hasAccess: true as const,
553-
};
556+
return undefined;
554557
}
555558
});
556559

560+
if (result.err || result.val === undefined) {
561+
return {
562+
hasAccess: true as const,
563+
};
564+
}
565+
557566
return result.val;
558567
}
559568

0 commit comments

Comments
 (0)