@@ -218,6 +218,16 @@ export function bustBillingLimitCaches(organizationId: string) {
218218 invalidateBillingLimitCaches ( organizationId ) ;
219219}
220220
221+ /**
222+ * Clears the caches whose value is derived from the org's plan. Call after a
223+ * plan change — a downgrade can revoke SSO, and serving the previous decision
224+ * for the stale TTL would keep a surface open that the new plan doesn't allow.
225+ */
226+ function invalidatePlanDerivedCaches ( organizationId : string ) {
227+ platformCache . entitlement . remove ( organizationId ) . catch ( ( ) => { } ) ;
228+ platformCache . ssoEntitlement . remove ( organizationId ) . catch ( ( ) => { } ) ;
229+ }
230+
221231// Clear the cached promo-credits read so a just-granted code shows on the usage
222232// page immediately rather than after the stale TTL.
223233export function bustPromoCreditsCache ( organizationId : string ) {
@@ -543,7 +553,7 @@ export async function setPlan(
543553 case "free_connected" : {
544554 // Selecting Free provisions the plan directly, so any free result is a success.
545555 opts ?. invalidateBillingCache ?.( organization . id ) ;
546- platformCache . entitlement . remove ( organization . id ) . catch ( ( ) => { } ) ;
556+ invalidatePlanDerivedCaches ( organization . id ) ;
547557 const response = redirect ( newProjectPath ( organization , "You're on the Free plan." ) ) ;
548558 await opts ?. onFreePlanProvisioned ?.( response ) ;
549559 return response ;
@@ -554,13 +564,13 @@ export async function setPlan(
554564 case "updated_subscription" : {
555565 // Invalidate billing cache since subscription changed
556566 opts ?. invalidateBillingCache ?.( organization . id ) ;
557- platformCache . entitlement . remove ( organization . id ) . catch ( ( ) => { } ) ;
567+ invalidatePlanDerivedCaches ( organization . id ) ;
558568 return redirectWithSuccessMessage ( callerPath , request , "Subscription updated successfully." ) ;
559569 }
560570 case "canceled_subscription" : {
561571 // Invalidate billing cache since subscription was canceled
562572 opts ?. invalidateBillingCache ?.( organization . id ) ;
563- platformCache . entitlement . remove ( organization . id ) . catch ( ( ) => { } ) ;
573+ invalidatePlanDerivedCaches ( organization . id ) ;
564574 return redirectWithSuccessMessage ( callerPath , request , "Subscription canceled." ) ;
565575 }
566576 }
@@ -778,28 +788,35 @@ export type SsoEntitlement = "entitled" | "not_entitled" | "unknown";
778788 * Loader errors are swallowed inside the loader for the same reason as
779789 * `getEntitlement`: @unkey/cache passes the loader promise to waitUntil()
780790 * with no .catch(), and returning undefined stops a transient billing
781- * failure from being cached as an access decision.
791+ * failure from being cached as an access decision. The SWR read is guarded
792+ * too, so a cache-infra failure resolves to `unknown` rather than rejecting
793+ * into the settings loader and the directory-sync worker.
782794 */
783795export async function getSsoEntitlement ( organizationId : string ) : Promise < SsoEntitlement > {
784796 if ( ! client ) return "entitled" ;
785797
786- const result = await platformCache . ssoEntitlement . swr ( organizationId , async ( ) => {
787- try {
788- const response = await client . currentPlan ( organizationId ) ;
789- if ( ! response . success ) {
790- recordPlatformFailure ( "getSsoEntitlement" , "no_success" ) ;
798+ try {
799+ const result = await platformCache . ssoEntitlement . swr ( organizationId , async ( ) => {
800+ try {
801+ const response = await client . currentPlan ( organizationId ) ;
802+ if ( ! response . success ) {
803+ recordPlatformFailure ( "getSsoEntitlement" , "no_success" ) ;
804+ return undefined ;
805+ }
806+ return response . v3Subscription ?. plan ?. limits ?. hasSso === true ;
807+ } catch ( _e ) {
808+ recordPlatformFailure ( "getSsoEntitlement" , "caught" ) ;
791809 return undefined ;
792810 }
793- return response . v3Subscription ?. plan ?. limits ?. hasSso === true ;
794- } catch ( _e ) {
795- recordPlatformFailure ( "getSsoEntitlement" , "caught" ) ;
796- return undefined ;
797- }
798- } ) ;
811+ } ) ;
799812
800- if ( result . err || result . val === undefined ) return "unknown" ;
813+ if ( result . err || result . val === undefined ) return "unknown" ;
801814
802- return result . val ? "entitled" : "not_entitled" ;
815+ return result . val ? "entitled" : "not_entitled" ;
816+ } catch ( _e ) {
817+ recordPlatformFailure ( "getSsoEntitlement" , "caught" ) ;
818+ return "unknown" ;
819+ }
803820}
804821
805822export type PromoCreditsData = {
0 commit comments