bitwarden/server · error · BadRequestException

All existing sends must be included in the rotation.

Error message

All existing sends must be included in the rotation.

What it means

Thrown by SendRotationValidator during key rotation. It loads every Bitwarden Send the user owns (each Send's name and data are encrypted with the user key) and requires the rotation request to include each one matched by Id. A missing Send would stay encrypted under the old key and become inaccessible, so the rotation is rejected.

Source

Thrown at src/Api/KeyManagement/Validators/SendRotationValidator.cs:44

        _sendRepository = sendRepository;
    }

    public async Task<IReadOnlyList<Send>> ValidateAsync(User user, IEnumerable<SendWithIdRequestModel> sends)
    {
        var result = new List<Send>();

        var existingSends = await _sendRepository.GetManyByUserIdAsync(user.Id);
        if (existingSends == null || existingSends.Count == 0)
        {
            return result;
        }

        foreach (var existing in existingSends)
        {
            var send = sends.FirstOrDefault(c => c.Id == existing.Id);
            if (send == null)
            {
                throw new BadRequestException("All existing sends must be included in the rotation.");
            }

            result.Add(send.UpdateSend(existing, _sendAuthorizationService));
        }

        return result;
    }
}

View on GitHub (pinned to e93b962371)

Solutions

  1. Sync the vault/Sends so the client knows about all Sends immediately before building the rotation request.
  2. Include every Send Id the user has, re-encrypted with the new key.
  3. Delete any unwanted Sends before rotating rather than omitting them.
  4. Validate the submitted Send Id set is a superset of the server set before sending.

Example fix

// before
const sends = localSends.map(s => reencryptSend(s));

// after
await syncSends();
const sends = allSends.map(s => reencryptSend(s));
Defensive patterns

Strategy: validation

Validate before calling

const sends = await api.getSends();
const submitted = new Set(payload.sends.map(s => s.id));
const missing = sends.filter(s => !submitted.has(s.id));
if (missing.length) {
  throw new Error(`Rotation is missing sends: ${missing.map(s => s.id).join(', ')}`);
}

Type guard

function isCompleteSendRotation(existing: { id: string }[], submitted: { id: string }[]): boolean {
  const have = new Set(submitted.map(s => s.id));
  return existing.every(s => have.has(s.id));
}

Try / catch

try {
  await api.rotateKey(payload);
} catch (e) {
  if (e.status === 400 && /sends must be included/i.test(e.message)) {
    await syncSends();
    payload.sends = allSends.map(s => reencryptSend(s));
    return api.rotateKey(payload);
  }
  throw e;
}

Prevention

When it happens

Trigger: Key-rotation request whose sends array omits a Send.Id that exists for the user. A Send was created in another session after the client cached the list; the client sent a partial list; an Id was malformed.

Common situations: User created a Send on another device and then rotated keys here; client enumerated sends from local state that was stale; a sync race during rotation.

Related errors


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