Budibase/budibase · error
Offline license has expired. expireAt=${license.expireAt}
Error message
Offline license has expired. expireAt=${license.expireAt} What it means
verifyExpiry checks an OfflineLicense's expireAt against the current time and throws this plain Error if the timestamp is unparseable (NaN) or in the past. It is the internal expiry guard for offline license tokens; verifyOfflineLicenseToken catches it and rethrows an HTTPError('Offline license has expired', 400).
Source
Thrown at packages/pro/src/sdk/licensing/licenses/offline/offline.ts:72
export async function getIdentifierBase64(): Promise<string> {
const identifier = await getIdentifier()
return encoding.objectToBase64(identifier)
}
export function getIdentifierFromBase64(
identifierBase64: string
): OfflineIdentifier {
return encoding.base64ToObject(identifierBase64)
}
// LICENSE
export function verifyExpiry(license: OfflineLicense) {
const now = Date.now()
const expireAt = new Date(license.expireAt).getTime()
if (!Number.isFinite(expireAt) || now > expireAt) {
throw new Error(`Offline license has expired. expireAt=${license.expireAt}`)
}
}
export class OfflineLicenseMismatchError extends Error {}
export async function verifyInstallation(license: OfflineLicense) {
const identifier = await getIdentifier()
if (
license.identifier.installId !== identifier.installId ||
license.identifier.tenantId !== identifier.tenantId
) {
// be intentionally vague
throw new OfflineLicenseMismatchError("Invalid offline license")
}
}
export function enrichLicense(license: OfflineLicense) {
const planType = license.plan.typeView on GitHub (pinned to a81a902e9a)
Solutions
- Renew the subscription and generate/download a fresh offline license token from the account portal.
- Verify the server clock is correct (NTP-synced) — a wrong clock can wrongly expire or extend licenses.
- Re-activate with a new token: activateOfflineLicenseToken(newToken), which refreshes the license cache.
- Decode the token to inspect expireAt if you suspect the token itself is malformed.
Example fix
// before
await activateOfflineLicenseToken(staleToken)
// after
const license = signing.decodeToken(staleToken)
if (new Date(license.expireAt).getTime() <= Date.now()) {
throw new Error("Token expired — download a fresh offline license from the account portal")
}
await activateOfflineLicenseToken(staleToken) Defensive patterns
Strategy: validation
Validate before calling
function isLicenseCurrent(license: { expireAt: string }): boolean {
const expireAt = new Date(license.expireAt).getTime()
return Number.isFinite(expireAt) && Date.now() <= expireAt
}
if (!isLicenseCurrent(license)) throw new Error("Offline license expired — renew before use") Type guard
function isValidDate(v: unknown): v is string {
return typeof v === "string" && Number.isFinite(new Date(v).getTime())
} Try / catch
try {
const license = await getOfflineLicense()
} catch (e) {
if (e instanceof Error && e.message.includes("has expired")) {
// trigger renewal flow / alert ops to refresh the token
} else throw e
} Prevention
- Set renewal reminders ahead of license expireAt dates.
- Keep servers NTP-synced so expiry checks are accurate.
- Track expireAt of stored offline tokens and alert before expiry.
- Re-export offline tokens after subscription renewals.
When it happens
Trigger: verifyExpiry(license) called (directly or via verifyOfflineLicenseToken → getOfflineLicense → cache.refresh) when license.expireAt is a past date or not a valid date string.
Common situations: Offline license naturally aged past its renewal date (typically annual); system clock wrong (far future makes tokens 'valid' past true expiry, far past expires fresh tokens); truncated/corrupted token payload losing the expireAt field; token copied from an old expired export.
Related errors
- Invalid offline license token
- Offline license has expired
- Features do not exist for planType=${planType}
- Invalid offline license
- Offline license does not match this installation
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/e3b0b7d8ab793292.
Report an issue: GitHub.