signalapp/Signal-Server · error · BackupWrongCredentialTypeException
wrong credential type for the requested operation
Error message
wrong credential type for the requested operation
What it means
BackupWrongCredentialTypeException thrown by BackupManager.checkBackupCredentialType when the AuthenticatedBackupUser's credential type (MEDIA or MESSAGES) differs from the type the operation requires. Backup credentials are scoped: media operations demand MEDIA credentials and message-backup operations demand MESSAGES credentials. A 'credential_type' authorization-failure counter is incremented.
Solutions
- Authenticate separately for each credential type and use the MEDIA credential for media endpoints and MESSAGES for message-backup endpoints
- Store credentials keyed by BackupCredentialType instead of a single cached token
- Check backupUser.credentialType() before issuing the request and switch credentials if needed
- Update clients that predate credential-type separation
Example fix
// before authenticate(messagesCredential); copyMedia(...); // wrong credential type for the requested operation // after authenticate(mediaCredential); copyMedia(...);
Defensive patterns
Strategy: validation
Validate before calling
if (backupUser.credentialType() != BackupCredentialType.MEDIA) {
throw new IllegalStateException("media endpoint requires a MEDIA credential");
} Try / catch
try { copyMedia(backupUser, toCopy); }
catch (BackupWrongCredentialTypeException e) {
backupUser = authenticateForType(BackupCredentialType.MEDIA);
copyMedia(backupUser, toCopy); // re-authenticate with correct type and retry once
} Prevention
- Key cached backup credentials by BackupCredentialType
- Authenticate through the endpoint matching the operation's credential type
- Assert credential type at the boundary of each media/messages flow
- Update clients predating MEDIA/MESSAGES credential separation
When it happens
Trigger: Calling a MEDIA-gated endpoint (e.g. media copy/quota, cdn read auth) while authenticated with a MESSAGES credential, or vice versa — typically after authenticating against the wrong backup-auth endpoint for the operation being attempted.
Common situations: Client caching one backup auth result and reusing it across both media and message-backup flows; MESSAGES (SVRB) flows accidentally invoking media upload helpers; refactors that swap the credential type used to authenticate; older clients where types were not separated.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Only primary device can set backup-id
- credential does not support the requested operation
- Must set at least one of message/media credential requests
- receipt credential presentation verification failed
- Invalid sourceObject size
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/5b380c165c7b0ab7.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupManager.java:801
}
}
/**
* Check that the authenticated backup user is authenticated with the given credential type
*
* @param backupUser The backup user to check
* @param credentialType The credential type to require
* @throws BackupWrongCredentialTypeException error if the backup user is not authenticated with the given
* {@code credentialType}
*/
@VisibleForTesting
static void checkBackupCredentialType(final AuthenticatedBackupUser backupUser, final BackupCredentialType credentialType) throws BackupWrongCredentialTypeException {
if (backupUser.credentialType() != credentialType) {
Metrics.counter(ZK_AUTHZ_FAILURE_COUNTER_NAME,
FAILURE_REASON_TAG_NAME, "credential_type")
.increment();
throw new BackupWrongCredentialTypeException("wrong credential type for the requested operation");
}
}
@VisibleForTesting
static String encodeMediaIdForCdn(final byte[] bytes) {
return Base64.getUrlEncoder().encodeToString(bytes);
}
private static byte[] decodeMediaIdFromCdn(final String base64) {
return Base64.getUrlDecoder().decode(base64);
}
private static String cdnMessageBackupName(final AuthenticatedBackupUser backupUser) {
return "%s/%s".formatted(backupUser.backupDir(), MESSAGE_BACKUP_NAME);
}
private static String cdnMediaDirectory(final String backupDir, final String mediaDir) {
return "%s/%s/".formatted(backupDir, mediaDir);View on GitHub (pinned to 100ab61c82)