signalapp/Signal-Server · error · BackupInvalidArgumentException
unknown cdn
Error message
unknown cdn
What it means
BackupInvalidArgumentException thrown by BackupManager.generateReadAuth when cdnNumber is not 3. Read authorization for backup media is only issued for CDN 3 (the CDN backing Signal backups); other CDN numbers are unknown to this code path. The javadoc explicitly documents this as an invalid cdnNumber input.
Solutions
- Pass cdnNumber 3 for all backup read-auth requests
- Route legacy attachment downloads (cdn 0/2) through the attachments code path instead of the backup path
- Parse the cdn number from the current server-provided backup descriptor rather than hardcoding it
- Update outdated clients that predate backup CDN 3
Example fix
// before
int cdn = attachment.cdn; // 0 for legacy attachments
generateReadAuth(backupUser, cdn); // 400: unknown cdn
// after
if (cdn == 3) {
generateReadAuth(backupUser, cdn);
} else {
downloadViaLegacyAttachmentApi(attachment);
} Defensive patterns
Strategy: validation
Validate before calling
if (cdnNumber != 3) {
throw new IllegalArgumentException("backup read auth requires cdn 3, got " + cdnNumber);
} Try / catch
try { generateReadAuth(backupUser, cdn); }
catch (BackupInvalidArgumentException e) {
if (e.getMessage().contains("unknown cdn")) { useLegacyAttachmentDownload(); } else { throw e; }
} Prevention
- Hardcode cdn 3 only in backup-specific code paths
- Parse cdn numbers from current server-provided descriptors
- Do not share one download helper between legacy attachments and backups
- Update constants when the server adds new CDN numbers
When it happens
Trigger: Requesting read auth for backup objects while passing cdn=0, 1, or 2 — e.g. reusing constants from legacy attachment CDN paths, or echoing a cdn value parsed from an old backup descriptor.
Common situations: Client code sharing one 'download attachment' helper across legacy attachments (CDN 0/2) and backups (CDN 3); stale backup manifests referencing old CDN numbers; hardcoded CDN constants that drifted between client versions.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Only primary device can set backup-id
- Must set at least one of message/media credential requests
- receipt credential presentation verification failed
- Invalid sourceObject size
- backup auth credential presentation verification failed
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/d71253d6d185c2ad.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupManager.java:421
public record StorageDescriptor(int cdn, byte[] key) {}
public record StorageDescriptorWithLength(int cdn, byte[] key, long length) {}
/**
* Generate credentials that can be used to read from the backup CDN
*
* @param backupUser an already ZK authenticated backup user
* @param cdnNumber the cdn number to get backup credentials for
* @return A map of headers to include with CDN requests
* @throws BackupPermissionException if the credential does not have the correct level
* @throws BackupInvalidArgumentException if the provided cdnNumber is invalid
*/
public Map<String, String> generateReadAuth(final AuthenticatedBackupUser backupUser, final int cdnNumber)
throws BackupInvalidArgumentException, BackupPermissionException {
checkBackupLevel(backupUser, BackupLevel.FREE);
if (cdnNumber != 3) {
throw new BackupInvalidArgumentException("unknown cdn");
}
return cdn3BackupCredentialGenerator.readHeaders(backupUser.backupDir());
}
/**
* Generate credentials that can be used with SVRB
*
* @param backupUser an already ZK authenticated backup user
* @return the credential that may be used with SVRB
* @throws BackupPermissionException if the credential does not have the correct level
* @throws BackupWrongCredentialTypeException if the credential does not have the messages type
*/
public ExternalServiceCredentials generateSvrbAuth(final AuthenticatedBackupUser backupUser)
throws BackupPermissionException, BackupWrongCredentialTypeException {
checkBackupLevel(backupUser, BackupLevel.FREE);
// Clients may only use SVRB with their messages backup-id
checkBackupCredentialType(backupUser, BackupCredentialType.MESSAGES);
return secureValueRecoveryBCredentialsGenerator.generateFor(svrbIdentifier(backupUser));View on GitHub (pinned to 100ab61c82)