Budibase/budibase · error · HTTPError
Invalid offline license token
Error message
Invalid offline license token
What it means
verifyOfflineLicenseToken wraps signing.verifyLicenseToken and, if signature/decoding verification fails for any reason, throws HTTPError('Invalid offline license token', 400). It means the token string is not a validly signed offline license: bad signature, malformed payload, wrong encoding, or tampering.
Source
Thrown at packages/pro/src/sdk/licensing/licenses/offline/offline.ts:109
const hosting = Hosting.SELF // offline only applicable in self host
const _features = features.getOfflineFeatures(planType)
const _quotas = quotas.getQuotas(hosting, planType)
// apply the latest features and quotas to license
license.features = union(license.features, _features)
license.quotas = merge(license.quotas, _quotas)
return license
}
export async function verifyOfflineLicenseToken(
token: string
): Promise<OfflineLicense> {
let license: OfflineLicense
try {
license = await signing.verifyLicenseToken(token)
} catch {
throw new HTTPError("Invalid offline license token", 400)
}
try {
verifyExpiry(license)
} catch {
throw new HTTPError("Offline license has expired", 400)
}
try {
await verifyInstallation(license)
} catch (e) {
if (e instanceof OfflineLicenseMismatchError) {
throw new HTTPError(
"Offline license does not match this installation",
400
)
}
throw eView on GitHub (pinned to a81a902e9a)
Solutions
- Re-copy the token exactly as provided by the account portal — no added whitespace, quotes, or line breaks.
- Download a fresh offline license token from the account portal for this installation's identifier.
- Ensure the Budibase/pro version matches the era the token was issued for; upgrade if formats changed.
- Verify you are passing the offline license token, not a license key or an API key.
Example fix
// before await activateOfflineLicenseKey(rawFileContents) // may include trailing newline/quotes // after const token = rawFileContents.trim().replace(/^"|"$/g, "") await activateOfflineLicenseToken(token)
Defensive patterns
Strategy: validation
Validate before calling
function looksLikeLicenseToken(token: string): boolean {
const t = token.trim()
return t.length > 0 && !t.includes("\n") && t.split(".").length >= 2
}
if (!looksLikeLicenseToken(token)) throw new Error("Not a valid offline license token format") Type guard
function isNonEmptyString(v: unknown): v is string {
return typeof v === "string" && v.trim().length > 0
} Try / catch
try {
await activateOfflineLicenseToken(token)
} catch (e) {
if (e instanceof HTTPError && e.status === 400 && e.message === "Invalid offline license token") {
// re-copy token from portal export; check for truncation/whitespace
} else throw e
} Prevention
- Copy tokens via file transfer rather than manual copy-paste where possible.
- Trim whitespace and surrounding quotes from token contents.
- Confirm the file holds the offline license token, not a license key.
- Keep Budibase versions aligned with the portal version that signed the token.
When it happens
Trigger: Calling verifyOfflineLicenseToken(token) (directly or via activateOfflineLicenseToken) with a token that fails cryptographic verification — truncated string, extra whitespace/newlines, altered payload, wrong signing key, or not a license token at all.
Common situations: Copy-paste errors when transferring the token file contents (missing characters, added quotes/whitespace); token from a different Budibase version with an incompatible format; attempting to use a JWT or other token type as an offline license; expired signing keys after an upgrade.
Related errors
- Offline license has expired. expireAt=${license.expireAt}
- Features do not exist for planType=${planType}
- Invalid offline license
- Offline license has expired
- Offline license does not match this installation
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/12c7406a7a95b3c0.
Report an issue: GitHub.