{"record":{"id":"e5f6cd73ab43ddb7","repo":"bitwarden/server","slug":"installationid-does-not-match-current-context","errorCode":null,"errorMessage":"InstallationId does not match current context.","messagePattern":"InstallationId does not match current context\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Platform/Push/Controllers/PushController.cs","lineNumber":94,"sourceCode":"        CheckUsage();\n        await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(\n            model.Devices.Select(d => Prefix(d.Id)),\n            Prefix(model.OrganizationId));\n    }\n\n    [HttpPost(\"send\")]\n    public async Task SendAsync([FromBody] PushSendRequestModel<JsonElement> model)\n    {\n        CheckUsage();\n\n        NotificationTarget target;\n        Guid targetId;\n\n        if (model.InstallationId.HasValue)\n        {\n            if (_currentContext.InstallationId!.Value != model.InstallationId.Value)\n            {\n                throw new BadRequestException(\"InstallationId does not match current context.\");\n            }\n\n            target = NotificationTarget.Installation;\n            targetId = _currentContext.InstallationId.Value;\n        }\n        else if (model.UserId.HasValue)\n        {\n            target = NotificationTarget.User;\n            targetId = model.UserId.Value;\n        }\n        else if (model.OrganizationId.HasValue)\n        {\n            target = NotificationTarget.Organization;\n            targetId = model.OrganizationId.Value;\n        }\n        else\n        {\n            throw new UnreachableException(\"Model validation should have prevented getting here.\");","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Platform/Push/Controllers/PushController.cs#L76-L112","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure the InstallationId in the request body matches the installation the caller authenticated as.","Omit InstallationId from the body and target by UserId or OrganizationId instead if installation-level broadcast is not intended.","Re-issue the installation token/credentials for the correct deployment and retry.","Log both the context InstallationId and the body InstallationId to confirm which is wrong."],"exampleFix":"// before: body carries a different installation id\n{ \"installationId\": \"<wrong-guid>\", \"type\": 0, ... }\n\n// after: match the authenticated installation, or target a user/org\n{ \"userId\": \"<user-guid>\", \"type\": 0, ... }","handlingStrategy":"validation","validationCode":"// Do not send an installationId that differs from the authenticated context\nif (body.installationId && body.installationId !== currentContext.installationId) {\n  throw new Error('Body installationId must match the authenticated installation');\n}","typeGuard":"function installationMatches(body: { installationId?: string }, ctx: { installationId?: string }): boolean {\n  return !body.installationId || body.installationId === ctx.installationId;\n}","tryCatchPattern":"try {\n  await api.pushSend(body);\n} catch (e) {\n  if (e.status === 400 && /InstallationId does not match/i.test(e.message)) {\n    delete body.installationId; // fall back to user/org targeting\n    return api.pushSend(body);\n  }\n  throw e;\n}","preventionTips":["Never hard-code an installationId in the request body; derive it from the auth context.","Prefer user/org targeting over installation targeting when possible.","Ensure the installation token matches the deployment you are calling."],"tags":["push","relay","installation","authentication","bad-request"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}