FlowiseAI/Flowise · error · InternalFlowiseError
Subscription is canceled
Error message
Subscription is canceled
What it means
UsageCacheManager.getSubscriptionDetails returns cached subscription details, falling back to a live Stripe retrieve on cache miss. If the retrieved subscription status is 'canceled', it throws InternalFlowiseError 401 before caching or returning. This is the cache layer's mirror of StripeManager's cancellation guard, so quota/feature code that reads subscription details also enforces entitlement.
Source
Thrown at packages/server/src/UsageCacheManager.ts:105
}
public async getSubscriptionDetails(subscriptionId: string, withoutCache: boolean = false): Promise<Record<string, any>> {
const stripeManager = await StripeManager.getInstance()
if (!stripeManager || !subscriptionId) {
return UNLIMITED_QUOTAS
}
// Skip cache if withoutCache is true
if (!withoutCache) {
const subscriptionData = await this.getSubscriptionDataFromCache(subscriptionId)
if (subscriptionData?.subsriptionDetails) {
return subscriptionData.subsriptionDetails
}
}
// If not in cache, retrieve from Stripe
const subscription = await stripeManager.getStripe().subscriptions.retrieve(subscriptionId)
if (subscription.status === 'canceled') throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, 'Subscription is canceled')
// Update subscription data cache
await this.updateSubscriptionDataToCache(subscriptionId, { subsriptionDetails: stripeManager.getSubscriptionObject(subscription) })
return stripeManager.getSubscriptionObject(subscription)
}
public async getQuotas(subscriptionId: string, withoutCache: boolean = false): Promise<Record<string, number>> {
const stripeManager = await StripeManager.getInstance()
if (!stripeManager || !subscriptionId) {
return UNLIMITED_QUOTAS
}
// Skip cache if withoutCache is true
if (!withoutCache) {
const subscriptionData = await this.getSubscriptionDataFromCache(subscriptionId)
if (subscriptionData?.quotas) {
return subscriptionData.quotasView on GitHub (pinned to abe4a8601a)
Solutions
- Have cancellation webhooks write a canceled marker into the cache so subsequent lookups short-circuit without throwing.
- Catch the 401 in callers and degrade to a disabled/free entitlement set.
- Filter canceled subscription IDs out of periodic usage jobs.
- Keep cached canceled status long-lived so the throw only happens once.
Example fix
// before
const subscription = await stripeManager.getStripe().subscriptions.retrieve(subscriptionId)
if (subscription.status === 'canceled') throw new InternalFlowiseError(StatusCodes.UNAUTHORIZED, 'Subscription is canceled')
// after: cache + return a canceled detail object instead of throwing
if (subscription.status === 'canceled') {
const details = stripeManager.getSubscriptionObject(subscription)
await this.updateSubscriptionDataToCache(subscriptionId, { subsriptionDetails: details })
return details
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check cached/live status to avoid the throw inside getSubscriptionDetails
const cached = await cacheManager.getSubscriptionDataFromCache(subscriptionId)
if (cached?.subsriptionDetails?.status === 'canceled') {
// degrade without triggering the live-retrieve throw
return cached.subsriptionDetails
} Type guard
function isSubscriptionActive(details: { status?: string } | undefined | null): boolean {
return !!details && details.status !== 'canceled'
} Try / catch
try {
const details = await cacheManager.getSubscriptionDetails(subscriptionId)
} catch (err) {
if (err instanceof InternalFlowiseError && err.statusCode === StatusCodes.UNAUTHORIZED) {
// subscription canceled: degrade to a disabled state
return { status: 'canceled', active: false }
}
throw err
} Prevention
- Write a canceled marker into the cache from cancellation webhooks so lookups short-circuit.
- Catch the 401 in callers and degrade to a disabled entitlement set.
- Exclude canceled subscription IDs from periodic usage jobs.
- Use a long TTL for cached canceled status so the throw happens at most once.
When it happens
Trigger: Any code path calling getSubscriptionDetails (directly or via quota/feature resolution) for a subscription Stripe reports canceled, after a cache miss forces the live retrieve.
Common situations: Background usage-sync job running against a just-canceled subscription. New deployment with an empty cache so the first request for a canceled sub hits Stripe. Cache eviction (TTL) expiring a previously-active entry after cancellation.
Related errors
- Subscription is canceled
- Stripe manager is not initialized
- No subscription items found
- Subscription ID is required
- No active price found for the product
AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12).
Data as JSON: /api/errors/681f6793f01a773c.
Report an issue: GitHub.