bitwarden/server · error · BadRequestException

Invalid license key.

Error message

Invalid license key.

What it means

Thrown (HTTP 400) by GET /licenses/user/{id}?key=... when the user exists but the supplied query key does not equal user.LicenseKey. A 2-second Task.Delay is applied before throwing to slow key enumeration. Note: a non-existent user returns null (HTTP 200 with null body), not this error.

Source

Thrown at src/Api/Billing/Controllers/LicensesController.cs:58

        _userService = userService;
        _organizationRepository = organizationRepository;
        _getCloudOrganizationLicenseQuery = getCloudOrganizationLicenseQuery;
        _validateBillingSyncKeyCommand = validateBillingSyncKeyCommand;
        _currentContext = currentContext;
    }

    [HttpGet("user/{id}")]
    public async Task<UserLicense> GetUser(string id, [FromQuery] string key)
    {
        var user = await _userRepository.GetByIdAsync(new Guid(id));
        if (user == null)
        {
            return null;
        }
        else if (!user.LicenseKey.Equals(key))
        {
            await Task.Delay(2000);
            throw new BadRequestException("Invalid license key.");
        }

        var license = await _userService.GenerateLicenseAsync(user, null);
        return license;
    }

    /// <summary>
    /// Used by self-hosted installations to get an updated license file
    /// </summary>
    [HttpGet("organization/{id}")]
    public async Task<OrganizationLicense> OrganizationSync(string id, [FromBody] SelfHostedOrganizationLicenseRequestModel model)
    {
        var organization = await _organizationRepository.GetByIdAsync(new Guid(id));
        if (organization == null)
        {
            throw new NotFoundException("Organization not found.");
        }

View on GitHub (pinned to e93b962371)

Solutions

  1. Retrieve the current license key from the user's billing settings in the cloud and use it as the ?key value.
  2. Update any stored configuration referencing the old key.
  3. Double-check there are no trailing spaces or encoding artifacts in the key.
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the configured license key is non-empty and matches the expected format before calling.
if (!licenseKey || licenseKey.length < EXPECTED_MIN_LEN) {
  throw new Error('License key is missing or truncated');
}

Try / catch

try {
  const lic = await get(`/licenses/user/${id}?key=${encodeURIComponent(key)}`);
} catch (e) {
  if (e.isBadRequest && /invalid license key/i.test(e.message)) {
    await refreshLicenseKeyFromCloud(); // fetch current key, then retry once
  } else { throw e; }
}

Prevention

When it happens

Trigger: Wrong or stale license key in the ?key= query parameter; the license key was regenerated; caller knows a valid user id but not its current key.

Common situations: License key rotated in the cloud portal but the self-hosted retrieval config still holds the old key; transcription/copy error in the key.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/b89ee476a4591fec. Report an issue: GitHub.