{"record":{"id":"936229d352c26ee4","repo":"bitwarden/server","slug":"invalid-license-936229","errorCode":null,"errorMessage":"Invalid license.","messagePattern":"Invalid license\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Billing/Controllers/VNext/SelfHostedAccountBillingVNextController.cs","lineNumber":30,"sourceCode":"\nnamespace Bit.Api.Billing.Controllers.VNext;\n\n[Authorize(\"Application\")]\n[Route(\"account/billing/vnext/self-host\")]\n[SelfHosted(SelfHostedOnly = true)]\npublic class SelfHostedAccountBillingVNextController(\n    ICreatePremiumSelfHostedSubscriptionCommand createPremiumSelfHostedSubscriptionCommand) : BaseBillingController\n{\n    [HttpPost(\"license\")]\n    [InjectUser]\n    public async Task<IResult> UploadLicenseAsync(\n        [BindNever] User user,\n        PremiumSelfHostedSubscriptionRequest request)\n    {\n        var license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, request.License);\n        if (license == null)\n        {\n            throw new BadRequestException(\"Invalid license.\");\n        }\n        var result = await createPremiumSelfHostedSubscriptionCommand.Run(user, license);\n        return Handle(result);\n    }\n}\n","sourceCodeStart":12,"sourceCodeEnd":36,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Billing/Controllers/VNext/SelfHostedAccountBillingVNextController.cs#L12-L36","documentation":"Thrown (HTTP 400) on the VNext self-hosted premium-subscription license endpoint when ApiHelpers.ReadJsonFileFromBody<UserLicense> returns null. Identical semantics to [264]: returns null for a missing file, a payload over 50KB, or JSON that does not deserialize into UserLicense (deserialization errors are silently caught).","triggerScenarios":"Non-JSON or binary file uploaded; truncated license JSON; an organization license where a user license is required; a license whose schema is from an incompatible version; file larger than 50KB.","commonSituations":"Cloud/self-hosted version skew where UserLicense fields changed; wrong license file downloaded; upload wrapper corrupts the JSON.","solutions":["Re-download the user license JSON from the cloud portal and upload the unmodified file.","Validate locally that the file is well-formed JSON with the expected UserLicense fields before uploading.","Confirm the file is under 50KB and is a user (not organization) license."],"exampleFix":"// before: upload blindly\nawait uploadLicenseVNext({ license: pickedFile });\n\n// after: validate first\nvar text = await pickedFile.text();\nvar parsed = JSON.parse(text); // throws on malformed JSON\nif (!parsed.licenseKey || !parsed.email) throw new Error('Not a valid user license');\nawait uploadLicenseVNext({ license: pickedFile });","handlingStrategy":"validation","validationCode":"// Validate the uploaded file is JSON with expected UserLicense fields before calling the VNext endpoint.\nconst text = await request.license.text();\nconst parsed = JSON.parse(text);\nif (!parsed.licenseKey || !parsed.email || !parsed.version) {\n  throw new Error('Not a valid Bitwarden user license');\n}","typeGuard":"// function isUserLicenseLike(o: unknown): o is { licenseKey: string; email: string; version: number } {\n//   return typeof o === 'object' && o !== null\n//     && typeof (o as any).licenseKey === 'string'\n//     && typeof (o as any).email === 'string'\n//     && typeof (o as any).version === 'number';\n// }","tryCatchPattern":"try {\n  await uploadLicenseVNext({ license: file });\n} catch (e) {\n  if (e.isBadRequest && /invalid license/i.test(e.message)) {\n    showUploadError('Re-download the user license from the cloud portal and retry.');\n  } else { throw e; }\n}","preventionTips":["Download the license JSON directly from the cloud portal without edits.","Validate JSON shape client-side before upload.","Ensure the file is a user license under 50KB."],"tags":["billing","license","self-hosted","validation","json","vnext"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}