{"record":{"id":"d71253d6d185c2ad","repo":"signalapp/Signal-Server","slug":"unknown-cdn","errorCode":null,"errorMessage":"unknown cdn","messagePattern":"unknown cdn","errorType":"exception","errorClass":"BackupInvalidArgumentException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupManager.java","lineNumber":421,"sourceCode":"\n  public record StorageDescriptor(int cdn, byte[] key) {}\n\n  public record StorageDescriptorWithLength(int cdn, byte[] key, long length) {}\n\n  /**\n   * Generate credentials that can be used to read from the backup CDN\n   *\n   * @param backupUser an already ZK authenticated backup user\n   * @param cdnNumber  the cdn number to get backup credentials for\n   * @return A map of headers to include with CDN requests\n   * @throws BackupPermissionException if the credential does not have the correct level\n   * @throws BackupInvalidArgumentException if the provided cdnNumber is invalid\n   */\n  public Map<String, String> generateReadAuth(final AuthenticatedBackupUser backupUser, final int cdnNumber)\n      throws BackupInvalidArgumentException, BackupPermissionException {\n    checkBackupLevel(backupUser, BackupLevel.FREE);\n    if (cdnNumber != 3) {\n      throw new BackupInvalidArgumentException(\"unknown cdn\");\n    }\n    return cdn3BackupCredentialGenerator.readHeaders(backupUser.backupDir());\n  }\n\n  /**\n   * Generate credentials that can be used with SVRB\n   *\n   * @param backupUser an already ZK authenticated backup user\n   * @return the credential that may be used with SVRB\n   * @throws BackupPermissionException if the credential does not have the correct level\n   * @throws BackupWrongCredentialTypeException if the credential does not have the messages type\n   */\n  public ExternalServiceCredentials generateSvrbAuth(final AuthenticatedBackupUser backupUser)\n      throws BackupPermissionException, BackupWrongCredentialTypeException {\n    checkBackupLevel(backupUser, BackupLevel.FREE);\n    // Clients may only use SVRB with their messages backup-id\n    checkBackupCredentialType(backupUser, BackupCredentialType.MESSAGES);\n    return secureValueRecoveryBCredentialsGenerator.generateFor(svrbIdentifier(backupUser));","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupManager.java#L403-L439","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nint cdn = attachment.cdn; // 0 for legacy attachments\ngenerateReadAuth(backupUser, cdn); // 400: unknown cdn\n// after\nif (cdn == 3) {\n  generateReadAuth(backupUser, cdn);\n} else {\n  downloadViaLegacyAttachmentApi(attachment);\n}","handlingStrategy":"validation","validationCode":"if (cdnNumber != 3) {\n  throw new IllegalArgumentException(\"backup read auth requires cdn 3, got \" + cdnNumber);\n}","typeGuard":null,"tryCatchPattern":"try { generateReadAuth(backupUser, cdn); }\ncatch (BackupInvalidArgumentException e) {\n  if (e.getMessage().contains(\"unknown cdn\")) { useLegacyAttachmentDownload(); } else { throw e; }\n}","preventionTips":["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"],"tags":["backup","cdn","invalid-argument"],"backgroundTag":"invalid-enum-value","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}