Budibase/budibase · critical · HTTPError
Error getting license: ${message}
Error message
Error getting license: ${message} What it means
When the licensing client fetches a tenant license from the licensing service, any non-200 response is converted into an HTTPError whose message embeds the upstream error text retrieved by getResponseErrorMessage, plus the upstream status code. A 404 (or a missing license response that isn't handled earlier) means the tenant has no license; 401/403 mean auth problems; 5xx mean the licensing service failed.
Source
Thrown at packages/pro/src/sdk/licensing/licenses/client.ts:133
tenantId: installTenantId,
version: installVersion,
},
}
try {
const response = await api.post(`/api/license`, {
headers: { ...authHeader },
body,
})
if (response.status === 404 || response.status === 403) {
// no license for the tenant
return
}
if (response.status !== 200) {
const message = await getResponseErrorMessage(response)
throw new HTTPError(
`Error getting license: ${message}`,
response.status
)
}
return response.json()
} catch (err) {
// account portal isn't present - no license found
if (env.DISABLE_ACCOUNT_PORTAL) {
return
}
throw err
}
}
)
})
}
View on GitHub (pinned to a81a902e9a)
Solutions
- Check the embedded status code: if 404, confirm the tenant actually has a license and the license key is correctly configured.
- If 401/403, fix the license key / API credentials used to authenticate with the licensing service.
- If 5xx or network errors, retry with backoff and check the licensing service status; wrap the call in retry logic.
- Refresh the cached license after fixing credentials so subsequent feature checks succeed.
Example fix
// before
const license = await licenses.getCachedLicense() // throws HTTPError
// after
let license
try {
license = await licenses.getCachedLicense()
} catch (err) {
if (err instanceof HTTPError && err.status === 404) {
license = null // no license for tenant — handle gracefully
} else {
throw err
}
} Defensive patterns
Strategy: retry
Validate before calling
const hasKey = !!env.SELF_HOSTED_LICENSE_KEY || !!licenseConfig
if (!hasKey) { /* skip license fetch; run in unlicensed mode */ } Type guard
function isLicenseHttpError(err: unknown): err is HTTPError {
return err instanceof HTTPError && typeof err.status === "number"
} Try / catch
try {
license = await getLicense()
} catch (err) {
if (err instanceof HTTPError) {
if (err.status === 404) { /* no license — degrade */ }
else if (err.status >= 500 || err.status === 429) { /* retry with backoff */ }
else { /* fix credentials/license key */ }
} else throw err
} Prevention
- Verify the license key env/config before startup and log its presence (not value)
- Implement retry with exponential backoff for 5xx/429 from the licensing service
- Handle 404 explicitly as 'unlicensed tenant' rather than a hard failure
- Monitor the licensing service and fall back to cached license when available
When it happens
Trigger: Calling _getLicense (via the client's get license path) when the licensing API responds with a non-200 status: unlicensed tenant (404), invalid/expired license key or credentials (401/403), or a licensing service outage (5xx).
Common situations: Self-hosted Budibase where SELF_HOSTED_LICENSE_KEY is unset or invalid; the licensing account service is unreachable/degraded; tenant id mismatch so the license lookup 404s; network/proxy issues causing gateway errors.
Related errors
- Unexpected response when fetching openid-configuration: ${re
- unexpected response ${response.statusText}
- Unexpected response ${response.statusText}
- Failed to retrieve skeleton metadata
- Unable to retrieve docker-compose file - ${response.status}
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/462d4e913e755db5.
Report an issue: GitHub.