bitwarden/server · error · BadRequestException

Organization mismatch. Re-sync if you recently moved this it

Error message

Organization mismatch. Re-sync if you recently moved this item, then try again.

What it means

PUT /ciphers/{id} (Put), after the EncryptedFor check, compares the stored `cipher.OrganizationId` against the `OrganizationId` in the request body. A mismatch means the client's view is stale — the item was shared into/out of an org — and continuing would write ciphertext under the wrong key scope, so the server returns HTTP 400.

Source

Thrown at src/Api/Vault/Controllers/CiphersController.cs:270

        // Validate the model was encrypted for the posting user
        if (model.EncryptedFor != null)
        {
            if (model.EncryptedFor != user.Id)
            {
                _logger.LogError("Cipher was not encrypted for the current user. CipherId: {CipherId}, CurrentUser: {CurrentUserId}, EncryptedFor: {EncryptedFor}", id, user.Id, model.EncryptedFor);
                throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
            }
        }

        ValidateClientVersionForFido2CredentialSupport(cipher);

        var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(user.Id, id)).Select(c => c.CollectionId).ToList();
        var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
            (Guid?)null : new Guid(model.OrganizationId);
        if (cipher.OrganizationId != modelOrgId)
        {
            throw new BadRequestException("Organization mismatch. Re-sync if you recently moved this item, " +
                "then try again.");
        }

        await _cipherService.SaveDetailsAsync(model.ToCipherDetails(cipher), user.Id, model.LastKnownRevisionDate, collectionIds);

        var response = new CipherResponseModel(cipher, user, await GetOrganizationAbilityAsync(cipher), _globalSettings);
        return response;
    }

    [HttpPost("{id}")]
    [Obsolete("This endpoint is deprecated. Use PUT method instead.")]
    public async Task<CipherResponseModel> PostPut(Guid id, [FromBody] CipherRequestModel model)
    {
        return await Put(id, model);
    }

    [HttpPut("{id}/admin")]
    public async Task<CipherMiniResponseModel> PutAdmin(Guid id, [FromBody] CipherRequestModel model)

View on GitHub (pinned to e93b962371)

Solutions

  1. Sync the vault (GET /sync or GET /ciphers) and retry the edit with the current OrganizationId.
  2. To move the item between scopes, use the share endpoint rather than a plain PUT.
  3. Before saving, confirm the OrganizationId in the payload equals the server's current value.

Example fix

// before: client thinks the cipher is still personal
PUT /ciphers/{id}  body: { "organizationId": null, ... }
// after: sync, then post the real org id
PUT /ciphers/{id}  body: { "organizationId": "<orgGuid>", ... }
Defensive patterns

Strategy: validation

Validate before calling

async function safePutCipher(id, payload) {
  const current = await api.get(`/ciphers/${id}`);
  if (payload.organizationId !== (current.organizationId ?? null)) {
    throw new Error('OrganizationId mismatch; sync before editing, or use the share endpoint to move.');
  }
  return api.put(`/ciphers/${id}`, payload);
}

Type guard

function orgIdMatches(payloadOrgId: string | null | undefined, serverOrgId: string | null): boolean {
  const p = payloadOrgId && payloadOrgId.trim() ? payloadOrgId : null;
  const s = serverOrgId ?? null;
  return p === s;
}

Try / catch

try { await api.put(`/ciphers/${id}`, payload); }
catch (e) {
  if (e?.response?.status === 400 && /Organization mismatch/i.test(e.response.data?.message ?? '')) {
    await syncVault(); // refresh OrganizationId, then retry once
  } else throw e;
}

Prevention

When it happens

Trigger: PUT /ciphers/{id} where the cipher was recently shared to an org (or removed from one) and the client posts the old OrganizationId (or null).

Common situations: The client was offline during a share/move; a race between two clients editing the same item; an org migration in progress; the client hasn't re-synced since the share.

Related errors


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