signalapp/Signal-Server · error · BadRequestException
Invalid length
Error message
Invalid length
What it means
After performing the media copy, the controller maps the CopyResult outcome SOURCE_WRONG_LENGTH to BadRequestException("Invalid length"): the source object stored on the CDN has a size that differs from the expected length supplied in the copy request, so the integrity-checked copy is refused.
Solutions
- Re-fetch the attachment's actual object size from the CDN and correct the length in CopyMediaObject before copying.
- Re-upload the source attachment if the stored object is truncated or corrupted, then retry the copy.
- Refresh local attachment metadata (size, digest) from the message sender or server.
- If the source is unrecoverable, skip the copy and treat the attachment as missing.
Example fix
// before copyRequest = new CopyMediaObject(oldMetadata.size, ...); // stale size -> 400 Invalid length // after long actualSize = headObject(sourceCdnKey).contentLength(); copyRequest = new CopyMediaObject(actualSize, ...);
Defensive patterns
Strategy: try-catch
Validate before calling
long actualSize = headSourceObject(cdn, key).contentLength();
if (actualSize != expectedLength) {
refreshMetadataThenCopy(); // fix length before calling copyMedia
} Try / catch
try {
copyMedia(request);
} catch (BadRequestException e) {
if ("Invalid length".equals(e.getMessage())) {
reFetchObjectSizeAndRetry();
} else throw e;
} Prevention
- Keep attachment size metadata in sync with actual CDN objects.
- Re-upload truncated or partially written source objects before copying.
- Verify sizes/digests when migrating between client versions.
When it happens
Trigger: Calling copyMedia with CopyMediaObject whose source length/expected plaintext length does not match the actual stored object's size.
Common situations: Client DB retaining stale attachment metadata (size recorded before a re-upload); partially uploaded or truncated source objects; migrating from an older client that recorded sizes differently; CDN objects modified or replaced.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- exceeded maximum uploadLength
- Source object not found
- Invalid create call link credential request
- Illegal timestamp
- Recipient list is empty
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/d5efe3c1ab181c27.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/ArchiveController.java:770
@NotNull
@Valid final ArchiveController.CopyMediaRequest copyMediaRequest)
throws BackupFailedZkAuthenticationException, BackupWrongCredentialTypeException, BackupPermissionException, BackupInvalidArgumentException {
if (account.isPresent()) {
throw new BadRequestException("must not use authenticated connection for anonymous operations");
}
final AuthenticatedBackupUser backupUser =
backupManager.authenticateBackupUser(presentation.presentation, signature.signature, userAgent);
final BackupManager.CopyQuota copyQuota =
backupManager.getCopyQuota(backupUser, List.of(copyMediaRequest.toCopyParameters()), maxAttachmentSize);
final CopyResult copyResult = backupManager.copyToBackup(copyQuota).next()
.blockOptional()
.orElseThrow(() -> new IllegalStateException("Non empty copy request must return result"));
backupMetrics.updateCopyCounter(copyResult, UserAgentTagUtil.getPlatformTag(userAgent));
return switch (copyResult.outcome()) {
case SUCCESS -> new CopyMediaResponse(copyResult.cdn());
case SOURCE_WRONG_LENGTH -> throw new BadRequestException("Invalid length");
case SOURCE_NOT_FOUND -> throw new ClientErrorException("Source object not found", Response.Status.GONE);
case OUT_OF_QUOTA ->
throw new ClientErrorException("Media quota exhausted", Response.Status.REQUEST_ENTITY_TOO_LARGE);
};
}
public record CopyMediaBatchRequest(
@Schema(description = "A list of media objects to copy from the attachments CDN to the backup CDN")
@NotNull
@Size(min = 1, max = 1000)
List<@Valid CopyMediaRequest> items) {}
public record CopyMediaBatchResponse(
@Schema(description = "Detailed outcome information for each copy request in the batch")
List<Entry> responses) {
public record Entry(View on GitHub (pinned to 100ab61c82)