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
- Check each sourceLength against the server's maximum object size before issuing the copy request and skip or split oversized objects
- Fix the client metadata source so lengths are real byte counts, never negative sentinels
- Compress or chunk large media before it becomes a source object
- 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
- Check byte length of every source object before copy requests
- Reject negative sentinel lengths at metadata parse time
- Compress or chunk media larger than the configured maximum
- Keep client size limits aligned with server backupConfiguration
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
- Only primary device can set backup-id
- Must set at least one of message/media credential requests
- receipt credential presentation verification failed
- unknown cdn
- backup auth credential presentation verification failed
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)