Budibase/budibase · critical
License not found for tenant id ${tenantId}
Error message
License not found for tenant id ${tenantId} What it means
getLicensedQuota resolves quota values from the tenant's license, which is obtained through the licensing cache. If no license is present in the cache for the tenant (getTenantId()), it cannot resolve any quota and throws with the tenant id embedded for diagnostics. Quota checks are meaningless without a license, so the function fails fast.
Source
Thrown at packages/pro/src/sdk/quotas/quotas.ts:463
opts: { ...action.opts, tenantId },
}))
if (usageQuotas.length > 0) {
await db.quotas.setAllUsage(usageQuotas)
}
})
}
export const getLicensedQuota = async (
quotaType: QuotaType,
name: MonthlyQuotaName | StaticQuotaName | ConstantQuotaName,
usageType?: QuotaUsageType
): Promise<Quota> => {
const license = await licensing.cache.getCachedLicense()
if (!license) {
const tenantId = tenancy.getTenantId()
throw new Error("License not found for tenant id " + tenantId)
}
if (usageType && isStaticQuota(quotaType, usageType, name)) {
return license.quotas[quotaType as QuotaType.USAGE][
usageType as QuotaUsageType.STATIC
][name]
} else if (usageType && isMonthlyQuota(quotaType, usageType, name)) {
return license.quotas[quotaType as QuotaType.USAGE][
usageType as QuotaUsageType.MONTHLY
][name]
} else if (isConstantQuota(quotaType, name)) {
return license.quotas[quotaType as QuotaType.CONSTANT][name]
} else {
throw new Error("Invalid quota type")
}
}
export const usageLimitIsExceeded = async ({View on GitHub (pinned to a81a902e9a)
Solutions
- Activate/re-upload a valid license for the tenant so getCachedLicense() can hydrate
- Verify connectivity to the licensing service and force a license cache refresh, then retry
- Confirm the tenantId is correct (log/compare with the id in the error message)
- Check Redis/licensing cache health; restart the cache rehydration if the license exists but isn't cached
Example fix
// before: quota updates before the license is activated
await quotas.tryIncrement({ name: MonthlyQuotaName.ROWS, usageChange: 1 })
// after: ensure a license exists first
const license = await licensing.cache.getCachedLicense()
if (!license) {
await activateLicense(licenseKey)
}
await quotas.tryIncrement({ name: MonthlyQuotaName.ROWS, usageChange: 1 }) Defensive patterns
Strategy: validation
Validate before calling
const license = await licensing.cache.getCachedLicense()
if (!license) throw new Error(`Activate a license for tenant ${tenancy.getTenantId()} before quota operations`) Type guard
function isLicenseMissing(err: unknown): boolean {
return err instanceof Error && err.message.startsWith("License not found for tenant id")
} Try / catch
try {
await updateUsage(action)
} catch (err) {
if (isLicenseMissing(err)) {
// trigger license activation / cache rehydration, then retry once
} else { throw err }
} Prevention
- Activate a license before enabling quota-gated features
- Monitor licensing service connectivity and cache health
- Log tenantId alongside license errors to catch mismatches early
When it happens
Trigger: updateUsage or any quota resolution runs for a tenantId whose license is not in the licensing cache — license never activated, license fetch from licensing service failed, or the cache was cleared/expired and rehydration failed.
Common situations: Self-hosted install without an active license key attempting quota-gated operations; licensing service outage or network failure preventing license hydration into cache; tenant id mismatch (requesting quotas under a tenant that has no license record); Redis cache flush without license re-fetch.
Related errors
- Invalid offline license
- Offline license does not match this installation
- Audit logs not available - license required.
- User does not have access to environment variables feature.
- FEATURE_DISABLED
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/181dc3785405ec5d.
Report an issue: GitHub.