bitwarden/server · error · BadRequestException

InstallationId does not match current context.

Error message

InstallationId does not match current context.

What it means

Thrown by PushController.SendAsync (POST /push/send) when the request body carries an InstallationId that does not equal the InstallationId established in the authenticated current context. The push relay must target the installation the caller is authenticated as, so a mismatch is rejected as a 400.

Source

Thrown at src/Api/Platform/Push/Controllers/PushController.cs:94

        CheckUsage();
        await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
            model.Devices.Select(d => Prefix(d.Id)),
            Prefix(model.OrganizationId));
    }

    [HttpPost("send")]
    public async Task SendAsync([FromBody] PushSendRequestModel<JsonElement> model)
    {
        CheckUsage();

        NotificationTarget target;
        Guid targetId;

        if (model.InstallationId.HasValue)
        {
            if (_currentContext.InstallationId!.Value != model.InstallationId.Value)
            {
                throw new BadRequestException("InstallationId does not match current context.");
            }

            target = NotificationTarget.Installation;
            targetId = _currentContext.InstallationId.Value;
        }
        else if (model.UserId.HasValue)
        {
            target = NotificationTarget.User;
            targetId = model.UserId.Value;
        }
        else if (model.OrganizationId.HasValue)
        {
            target = NotificationTarget.Organization;
            targetId = model.OrganizationId.Value;
        }
        else
        {
            throw new UnreachableException("Model validation should have prevented getting here.");

View on GitHub (pinned to e93b962371)

Solutions

  1. Ensure the InstallationId in the request body matches the installation the caller authenticated as.
  2. Omit InstallationId from the body and target by UserId or OrganizationId instead if installation-level broadcast is not intended.
  3. Re-issue the installation token/credentials for the correct deployment and retry.
  4. Log both the context InstallationId and the body InstallationId to confirm which is wrong.

Example fix

// before: body carries a different installation id
{ "installationId": "<wrong-guid>", "type": 0, ... }

// after: match the authenticated installation, or target a user/org
{ "userId": "<user-guid>", "type": 0, ... }
Defensive patterns

Strategy: validation

Validate before calling

// Do not send an installationId that differs from the authenticated context
if (body.installationId && body.installationId !== currentContext.installationId) {
  throw new Error('Body installationId must match the authenticated installation');
}

Type guard

function installationMatches(body: { installationId?: string }, ctx: { installationId?: string }): boolean {
  return !body.installationId || body.installationId === ctx.installationId;
}

Try / catch

try {
  await api.pushSend(body);
} catch (e) {
  if (e.status === 400 && /InstallationId does not match/i.test(e.message)) {
    delete body.installationId; // fall back to user/org targeting
    return api.pushSend(body);
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /push/send with a JSON body whose InstallationId field is set to a value different from the installation the request was authenticated under (the installation claim in the auth token/context).

Common situations: Client mixed up installation credentials across deployments (e.g. sent a cloud installation id while authenticated as a different installation); a stale token from a previous installation; the body was templated with the wrong id.

Related errors


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