signalapp/Signal-Server · error · ClientErrorException
exceeded maximum uploadLength
Error message
exceeded maximum uploadLength
What it means
AttachmentControllerV4.getAttachmentUploadForm validates the requested uploadLength against the server-configured maximum. If the requested byte size exceeds maxUploadLength, it returns HTTP 413 'exceeded maximum uploadLength' and no upload form/credentials are issued.
Solutions
- Check file size client-side and refuse/trim uploads above the known max before requesting a form
- Reduce the attachment size (compress, downscale video, split files) and retry
- If the limit is unexpectedly low, verify the server's maxUploadLength configuration
Example fix
// before
long size = file.length();
String form = api.getAttachmentUploadForm(size);
// after
if (file.length() > MAX_UPLOAD_LENGTH) {
throw new FileTooLargeException(file.length(), MAX_UPLOAD_LENGTH);
}
String form = api.getAttachmentUploadForm(file.length()); Defensive patterns
Strategy: validation
Validate before calling
if (file.length() > MAX_UPLOAD_LENGTH) { throw new IllegalArgumentException("file exceeds max upload length: " + file.length()); } Try / catch
try { requestUploadForm(size); } catch (WebApplicationException e) { if (e.getResponse().getStatus() == 413) { compressOrReject(file); } } Prevention
- Check file size before requesting an upload form
- Keep the client-side max in sync with server config
- Compress large media proactively
When it happens
Trigger: GET the attachment upload form with ?uploadLength=N where N > server maxUploadLength; large video/file uploads where the client does not clamp the size before requesting a form.
Common situations: Uploading media larger than the deployment's configured cap; server max lowered via config while clients still attempt old large uploads; client sending bytes instead of the expected unit.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- exceeded maximum uploadLength
- Blank header
- end of range must be after start of range
- timestamps must be day aligned
- start of range too far in the past
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/d7d6c068505f856e.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV4.java:111
Uploads with the returned form will be limited to a maximum size of the provided uploadLength.
"""
)
@ApiResponse(responseCode = "200", description = "Success, response body includes upload form", useReturnTypeSchema = true)
@ApiResponse(responseCode = "400", description = "The provided uploadLength was not valid")
@ApiResponse(responseCode = "413", description = "The provided uploadLength is larger than the maximum supported upload size. The maximum upload size is subject to change and is governed by `global.attachments.maxBytes`.")
@ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header(
name = "Retry-After",
description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed"))
public AttachmentDescriptorV3 getAttachmentUploadForm(
@Auth AuthenticatedDevice auth,
@Parameter(description = "The size of the attachment to upload in bytes")
@QueryParam("uploadLength") final @Valid Optional<@Positive Long> maybeUploadLength,
@HeaderParam(HttpHeaders.USER_AGENT) @Nullable final String userAgent)
throws RateLimitExceededException {
final long uploadLength = maybeUploadLength.orElse(maxUploadLength);
if (uploadLength > maxUploadLength) {
throw new ClientErrorException("exceeded maximum uploadLength", Response.Status.REQUEST_ENTITY_TOO_LARGE);
}
countRateLimiter.validate(auth.accountIdentifier());
if (maybeUploadLength.isPresent()) {
// Ideally we'd check these two rate limits transactionally and only update them if both permits were acquired.
// However, just undoing the first modification if the second one fails is close enough for our purposes
try {
bytesRateLimiter.validate(auth.accountIdentifier(), maybeUploadLength.get());
} catch (RateLimitExceededException e) {
countRateLimiter.restorePermits(auth.accountIdentifier(), 1);
throw e;
}
}
DistributionSummary.builder(ATTACHMENT_SIZE_NAME)
.tags(Tags.of(UserAgentTagUtil.getPlatformTag(userAgent),
Tag.of("uploadLengthSupplied", Boolean.toString(maybeUploadLength.isPresent()))))
.register(Metrics.globalRegistry)View on GitHub (pinned to 100ab61c82)