bitwarden/server · error · BadRequestException
Invalid license
Error message
Invalid license
What it means
Thrown (HTTP 400) on the self-hosted-only license upload endpoint when ApiHelpers.ReadJsonFileFromBody<UserLicense> returns null. That helper returns null when no file is present, the payload exceeds the 50KB maxSize, or JSON deserialization throws (silently caught) — so 'Invalid license' means the upload could not be parsed as a UserLicense, not that the license signature failed.
Source
Thrown at src/Api/Billing/Controllers/AccountsController.cs:51
/*
* TODO: A new version of this exists in the AccountBillingVNextController.
* The individual-self-hosting-license-uploader.component needs to be updated to use it.
* Then, this can be removed.
*/
[HttpPost("license")]
[SelfHosted(SelfHostedOnly = true)]
public async Task PostLicenseAsync(LicenseRequestModel model)
{
var user = await userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
if (license == null)
{
throw new BadRequestException("Invalid license");
}
await userService.UpdateLicenseAsync(user, license);
}
// TODO: Migrate to Command / AccountBillingVNextController as DELETE /account/billing/vnext/subscription
[HttpPost("cancel")]
public async Task PostCancelAsync(
[FromBody] SubscriptionCancellationRequestModel request,
[FromServices] ISubscriberService subscriberService)
{
var user = await userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
View on GitHub (pinned to e93b962371)
Solutions
- Re-download the user license JSON from the cloud portal and re-upload the unmodified file.
- Validate locally that the file parses as JSON and contains the expected UserLicense fields before uploading.
- Confirm the file is under 50KB and is a user (not organization) license.
Example fix
// before: upload whatever file the user picked
await postLicense({ license: file });
// after: validate JSON shape client-side first
var json = JSON.parse(await file.text());
if (!json.licenseKey || !json.email) throw new Error('Not a valid user license');
await postLicense({ license: file }); Defensive patterns
Strategy: validation
Validate before calling
// Validate the file is parseable JSON with expected UserLicense fields before uploading.
const text = await file.text();
const parsed = JSON.parse(text);
if (!parsed.licenseKey || !parsed.email || !parsed.version) {
throw new Error('File is not a valid Bitwarden user license');
} Type guard
// function isUserLicenseLike(o: unknown): o is { licenseKey: string; email: string } {
// return typeof o === 'object' && o !== null
// && typeof (o as any).licenseKey === 'string'
// && typeof (o as any).email === 'string';
// } Try / catch
try {
await postLicense({ license: file });
} catch (e) {
if (e.isBadRequest && /invalid license/i.test(e.message)) {
showUploadError('License file could not be read. Re-download it from the cloud portal.');
} else { throw e; }
} Prevention
- Download the license JSON directly from the cloud portal without manual edits.
- Confirm the file is a user license, not an organization license.
- Keep the file under 50KB and avoid re-wrapping it in another container.
When it happens
Trigger: Uploading a non-JSON or binary file; a truncated/malformed license JSON; an organization license where a user license is expected; a license whose schema is from an incompatible Bitwarden version; a file larger than 50KB.
Common situations: Wrong license type downloaded from the cloud portal; copy-paste corruption of the .json; cloud/server version skew where UserLicense fields changed; accidental upload of a .txt wrapper.
Related errors
- Invalid license.
- Failed to remove organization vault. Please contact support.
- Organization must have at least one confirmed owner.
- NoSeatsAvailable
- Organization Connection configuration malformed
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/31d5ef36227313ef.
Report an issue: GitHub.