signalapp/Signal-Server · error · BackupInvalidArgumentException

Invalid sourceObject size

Error message

Invalid sourceObject size

What it means

BackupInvalidArgumentException thrown by BackupManager.getCopyQuota when any CopyParameters entry has a sourceLength greater than the configured maximumSourceObjectSize or a negative length. The server validates every source object size up front so copy operations cannot bypass the quota system with oversized objects. Negative sizes are treated as corrupt input rather than 'unlimited'.

Solutions

  1. Check each sourceLength against the server's maximum object size before issuing the copy request and skip or split oversized objects
  2. Fix the client metadata source so lengths are real byte counts, never negative sentinels
  3. Compress or chunk large media before it becomes a source object
  4. If legitimate objects now exceed the limit, have the operator raise maximumSourceObjectSize in backup configuration

Example fix

// before
copy(List.of(new CopyParameters(messageId, hugeFileLength, destLen))); // 400: Invalid sourceObject size
// after
if (hugeFileLength >= 0 && hugeFileLength <= MAX_SOURCE_OBJECT_SIZE) {
  copy(List.of(new CopyParameters(messageId, hugeFileLength, destLen)));
} else {
  uploadFreshMediaCopy(hugeFile); // skip oversized source
}
Defensive patterns

Strategy: validation

Validate before calling

for (CopyParameters p : toCopy) {
  if (p.sourceLength() < 0 || p.sourceLength() > MAX_SOURCE_OBJECT_SIZE) {
    throw new IllegalArgumentException("sourceObject size out of range: " + p.sourceLength());
  }
}

Try / catch

try { getCopyQuota(backupUser, toCopy); }
catch (BackupInvalidArgumentException e) {
  if (e.getMessage().contains("sourceObject")) { splitOrReuploadOversizedObjects(toCopy); } else { throw e; }
}

Prevention

When it happens

Trigger: Calling the copy-quota/copy endpoint with source objects larger than the configured maximumSourceObjectSize, or with sourceLength serialized as -1 from corrupt or placeholder metadata.

Common situations: Copying very large videos between backup layers; deserializing attachment metadata with sentinel -1 lengths; clients computing length from truncated headers; server config lowering maximumSourceObjectSize below previously accepted objects.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/503e04de7ba00a76. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/backup/BackupManager.java:342

   *
   * @param toCopy     The proposed copy requests
   * @return QuotaResult indicating which requests fit into the remaining quota and which requests should be
   * rejected with {@link CopyResult.Outcome#OUT_OF_QUOTA}
   * @throws BackupInvalidArgumentException if toCopy contains an invalid copy request
   * @throws BackupPermissionException if the credential does not have the correct level
   * @throws BackupWrongCredentialTypeException if the credential does not have the media type
   */
  public CopyQuota getCopyQuota(
      final AuthenticatedBackupUser backupUser,
      final List<CopyParameters> toCopy,
      final long maximumSourceObjectSize)
      throws BackupWrongCredentialTypeException, BackupPermissionException, BackupInvalidArgumentException {
    checkBackupLevel(backupUser, BackupLevel.PAID);
    checkBackupCredentialType(backupUser, BackupCredentialType.MEDIA);

    for (CopyParameters copyParameters : toCopy) {
      if (copyParameters.sourceLength() > maximumSourceObjectSize || copyParameters.sourceLength() < 0) {
        throw new BackupInvalidArgumentException("Invalid sourceObject size");
      }
    }
    final long totalBytesAdded = toCopy.stream().mapToLong(CopyParameters::destinationObjectSize).sum();

    final Duration maxQuotaStaleness = backupConfiguration.maxQuotaStaleness();
    final long maxTotalMediaSize = backupConfiguration.maxTotalMediaSize();

    final BackupsDb.TimestampedUsageInfo info = backupsDb.getMediaUsage(backupUser).join();
    long estimatedRemainingQuota = maxTotalMediaSize - info.usageInfo().bytesUsed();
    final boolean canStore = estimatedRemainingQuota >= totalBytesAdded;
    if (canStore || info.lastRecalculationTime().isAfter(clock.instant().minus(maxQuotaStaleness))) {
      return CopyQuota.create(backupUser, toCopy, estimatedRemainingQuota);
    }

    // The user is out of quota, and we have not recently recalculated the user's usage. Double check by doing a
    // hard recalculation before actually forbidding the user from storing additional media.
    boolean usageChanged = false;
    try {

View on GitHub (pinned to 100ab61c82)