signalapp/Signal-Server · error · BackupInvalidArgumentException
Must set at least one of message/media credential requests
Error message
Must set at least one of message/media credential requests
What it means
commitBackupId requires at least one BackupAuthCredentialRequest: the caller must request credentials for message backups, media backups, or both. Supplying two empty Optionals is a client contract violation surfaced as BackupInvalidArgumentException.
Solutions
- Include at least one of the message or media BackupAuthCredentialRequest fields in the request body.
- Fix client serialization so present credential requests are not dropped as null.
- Validate the request payload before invoking the manager and return a 400 with a precise message.
Example fix
// before
backupAuthManager.commitBackupId(account, device, Optional.empty(), Optional.empty());
// after
if (messagesReq.isEmpty() && mediaReq.isEmpty()) {
throw new BadRequestException("must set at least one of message/media credential requests");
}
backupAuthManager.commitBackupId(account, device, messagesReq, mediaReq); Defensive patterns
Strategy: validation
Validate before calling
if (messagesReq.isEmpty() && mediaReq.isEmpty()) {
throw new BadRequestException("at least one of message/media credential requests is required");
} Type guard
boolean hasAtLeastOneCredentialRequest(Optional<BackupAuthCredentialRequest> m, Optional<BackupAuthCredentialRequest> med) {
return m.isPresent() || med.isPresent();
} Try / catch
try {
backupAuthManager.commitBackupId(account, device, messagesReq, mediaReq);
} catch (BackupInvalidArgumentException e) {
return Response.status(400).entity(e.getMessage()).build();
} Prevention
- Make the request schema require oneOf(messages, media) credential fields
- Verify client serializers do not drop empty-but-present fields as null
- Add contract tests covering both-only, either-only, and neither payloads
When it happens
Trigger: Calling commitBackupId / PUT /v1/backups/ with a request body omitting both messagesBackupCredentialRequest and mediaBackupCredentialRequest.
Common situations: Older clients predating the media-credential field sending empty payloads; clients serializing nullable fields incorrectly so both Optionals end up empty; API consumers hand-building JSON bodies.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Blank header
- end of range must be after start of range
- timestamps must be day aligned
- start of range too far in the past
- end of range too far in the future
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/4e84923ae95be752.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupAuthManager.java:111
* @param device The device setting the account backup-id
* @param messagesBackupCredentialRequest A request containing the blinded backup-id the client will use to upload
* message backups
* @param mediaBackupCredentialRequest A request containing the blinded backup-id the client will use to upload
* media backups
* @throws RateLimitExceededException If too many backup-ids have been committed
*/
public void commitBackupId(
final Account account,
final Device device,
final Optional<BackupAuthCredentialRequest> messagesBackupCredentialRequest,
final Optional<BackupAuthCredentialRequest> mediaBackupCredentialRequest)
throws RateLimitExceededException, BackupPermissionException, BackupInvalidArgumentException {
if (!device.isPrimary()) {
throw new BackupPermissionException("Only primary device can set backup-id");
}
if (messagesBackupCredentialRequest.isEmpty() && mediaBackupCredentialRequest.isEmpty()) {
throw new BackupInvalidArgumentException("Must set at least one of message/media credential requests");
}
final byte[] storedMessageCredentialRequest = account.getBackupCredentialRequest(BackupCredentialType.MESSAGES)
.orElse(null);
final byte[] storedMediaCredentialRequest = account.getBackupCredentialRequest(BackupCredentialType.MEDIA)
.orElse(null);
// If the provided credential request is null, we want to set to the existing request
final byte[] targetMessageCredentialRequest = messagesBackupCredentialRequest
.map(BackupAuthCredentialRequest::serialize)
.orElse(storedMessageCredentialRequest);
final byte[] targetMediaCredentialRequest = mediaBackupCredentialRequest
.map(BackupAuthCredentialRequest::serialize)
.orElse(storedMediaCredentialRequest);
final boolean requiresMessageRotation =
!MessageDigest.isEqual(targetMessageCredentialRequest, storedMessageCredentialRequest);
final boolean requiresMediaRotation =View on GitHub (pinned to 100ab61c82)