{"record":{"id":"3566e0f993405c91","repo":"bitwarden/server","slug":"unknown-organization-user","errorCode":null,"errorMessage":"Unknown Organization User","messagePattern":"Unknown Organization User","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Controllers/SelfHosted/SelfHostedOrganizationSponsorshipsController.cs","lineNumber":76,"sourceCode":"    {\n        await _offerSponsorshipCommand.CreateSponsorshipAsync(\n            await _organizationRepository.GetByIdAsync(sponsoringOrgId),\n            await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),\n            model.PlanSponsorshipType,\n            model.SponsoredEmail,\n            model.FriendlyName,\n            model.IsAdminInitiated.GetValueOrDefault(),\n            model.Notes);\n    }\n\n    [HttpDelete(\"{sponsoringOrgId}\")]\n    public async Task RevokeSponsorship(Guid sponsoringOrgId)\n    {\n        var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);\n\n        if (orgUser == null)\n        {\n            throw new BadRequestException(\"Unknown Organization User\");\n        }\n\n        var existingOrgSponsorship = await _organizationSponsorshipRepository\n            .GetBySponsoringOrganizationUserIdAsync(orgUser.Id);\n\n        await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);\n    }\n\n    [HttpPost(\"{sponsoringOrgId}/delete\")]\n    [Obsolete(\"This endpoint is deprecated. Use DELETE /{sponsoringOrgId} instead.\")]\n    public async Task PostRevokeSponsorship(Guid sponsoringOrgId)\n    {\n        await RevokeSponsorship(sponsoringOrgId);\n    }\n\n    [Authorize<ManageUsersRequirement>]\n    [HttpDelete(\"{organizationId}/{sponsoredFriendlyName}/revoke\")]\n    public async Task AdminInitiatedRevokeSponsorshipAsync([FromRoute(Name = \"organizationId\")] Guid sponsoringOrgId, string sponsoredFriendlyName)","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Controllers/SelfHosted/SelfHostedOrganizationSponsorshipsController.cs#L58-L94","documentation":"Thrown by DELETE /sponsorships/{sponsoringOrgId} (revoke sponsorship) when the calling user is not found as an organization user of the sponsoring org. The lookup GetByOrganizationAsync(sponsoringOrgId, currentContext.UserId) returns null, so there is no org-user record to anchor the revocation. It is a BadRequest (not 404) because the caller is authenticated but addressing the wrong org.","triggerScenarios":"A user who is not a member of sponsoringOrgId calls revoke-sponsorship on it; currentContext.UserId is null/default (no user principal); the org-user row was removed between sessions.","commonSituations":"User removed from the sponsoring org but still holds a token; client hard-codes the wrong sponsoringOrgId; admin testing with a service account that has no org-user membership; off-by-one copy of an org id.","solutions":["Confirm the caller is a member of sponsoringOrgId and that the id is correct.","Re-authenticate so currentContext.UserId resolves to the actual user.","Have an org admin (who is an org user) perform the revocation."],"exampleFix":"// before: caller is not a member of sponsoringOrgId\n//   DELETE /sponsorships/{wrongOrgId}\n//\n// after: revoke from an org the caller actually belongs to\nvar myOrgs = await GetMyOrganizationMembershipsAsync();\nvar sponsoringOrg = myOrgs.First(o => o.Id == sponsoringOrgId);\nawait RevokeSponsorshipAsync(sponsoringOrg.Id);","handlingStrategy":"validation","validationCode":"// Verify the caller is an org user of the sponsoring org before revoking\nconst myMemberships = await getMyOrgMemberships();\nif (!myMemberships.some(m => m.organizationId === sponsoringOrgId)) {\n  throw new Error('Caller is not a member of the sponsoring org');\n}\nawait revokeSponsorship(sponsoringOrgId);","typeGuard":"function isMemberOf(memberships, orgId) {\n  return Array.isArray(memberships) && memberships.some(m => m.organizationId === orgId);\n}","tryCatchPattern":"try {\n  await revokeSponsorship(sponsoringOrgId);\n} catch (e) {\n  if (e.status === 400 && /unknown organization user/i.test(e.message)) {\n    // caller is not a member; surface as 'access denied' or switch caller\n  } else throw e;\n}","preventionTips":["Have an org member (ideally admin) perform sponsorship revocations.","Confirm sponsoringOrgId is correct and that the user belongs to it.","Ensure currentContext.UserId resolves (re-auth if the token is stale)."],"tags":["sponsorships","self-hosted","organization-users","http-400","validation"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}