{"record":{"id":"d7d6c068505f856e","repo":"signalapp/Signal-Server","slug":"exceeded-maximum-uploadlength-d7d6c0","errorCode":null,"errorMessage":"exceeded maximum uploadLength","messagePattern":"exceeded maximum uploadLength","errorType":"http","errorClass":"ClientErrorException","httpStatus":413,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV4.java","lineNumber":111,"sourceCode":"          Uploads with the returned form will be limited to a maximum size of the provided uploadLength.\n          \"\"\"\n  )\n  @ApiResponse(responseCode = \"200\", description = \"Success, response body includes upload form\", useReturnTypeSchema = true)\n  @ApiResponse(responseCode = \"400\", description = \"The provided uploadLength was not valid\")\n  @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`.\")\n  @ApiResponse(responseCode = \"429\", description = \"Too many attempts\", headers = @Header(\n      name = \"Retry-After\",\n      description = \"If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed\"))\n  public AttachmentDescriptorV3 getAttachmentUploadForm(\n      @Auth AuthenticatedDevice auth,\n      @Parameter(description = \"The size of the attachment to upload in bytes\")\n      @QueryParam(\"uploadLength\") final @Valid Optional<@Positive Long> maybeUploadLength,\n      @HeaderParam(HttpHeaders.USER_AGENT) @Nullable final String userAgent)\n      throws RateLimitExceededException {\n\n    final long uploadLength = maybeUploadLength.orElse(maxUploadLength);\n    if (uploadLength > maxUploadLength) {\n      throw new ClientErrorException(\"exceeded maximum uploadLength\", Response.Status.REQUEST_ENTITY_TOO_LARGE);\n    }\n\n    countRateLimiter.validate(auth.accountIdentifier());\n    if (maybeUploadLength.isPresent()) {\n      // Ideally we'd check these two rate limits transactionally and only update them if both permits were acquired.\n      // However, just undoing the first modification if the second one fails is close enough for our purposes\n      try {\n        bytesRateLimiter.validate(auth.accountIdentifier(), maybeUploadLength.get());\n      } catch (RateLimitExceededException e) {\n        countRateLimiter.restorePermits(auth.accountIdentifier(), 1);\n        throw e;\n      }\n    }\n\n    DistributionSummary.builder(ATTACHMENT_SIZE_NAME)\n        .tags(Tags.of(UserAgentTagUtil.getPlatformTag(userAgent),\n            Tag.of(\"uploadLengthSupplied\", Boolean.toString(maybeUploadLength.isPresent()))))\n        .register(Metrics.globalRegistry)","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV4.java#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlong size = file.length();\nString form = api.getAttachmentUploadForm(size);\n// after\nif (file.length() > MAX_UPLOAD_LENGTH) {\n  throw new FileTooLargeException(file.length(), MAX_UPLOAD_LENGTH);\n}\nString form = api.getAttachmentUploadForm(file.length());","handlingStrategy":"validation","validationCode":"if (file.length() > MAX_UPLOAD_LENGTH) { throw new IllegalArgumentException(\"file exceeds max upload length: \" + file.length()); }","typeGuard":null,"tryCatchPattern":"try { requestUploadForm(size); } catch (WebApplicationException e) { if (e.getResponse().getStatus() == 413) { compressOrReject(file); } }","preventionTips":["Check file size before requesting an upload form","Keep the client-side max in sync with server config","Compress large media proactively"],"tags":["http-413","attachments","upload","validation"],"backgroundTag":"payload-too-large","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"}