{"record":{"id":"11c4ce8e5238c23f","repo":"signalapp/Signal-Server","slug":"recipient-list-is-empty","errorCode":null,"errorMessage":"Recipient list is empty","messagePattern":"Recipient list is empty","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java","lineNumber":496,"sourceCode":"      @Parameter(description=\"The sender's timestamp for the envelope\")\n      @QueryParam(\"ts\") long timestamp,\n\n      @Parameter(description=\"If true, this message should cause push notifications to be sent to recipients\")\n      @QueryParam(\"urgent\") @DefaultValue(\"true\") final boolean isUrgent,\n\n      @Parameter(description=\"If true, the message is a story; access tokens are not checked and sending to nonexistent recipients is permitted\")\n      @QueryParam(\"story\") boolean isStory,\n      @Parameter(description=\"The sealed-sender multi-recipient message payload as serialized by libsignal\")\n      @NotNull SealedSenderMultiRecipientMessage multiRecipientMessage,\n\n      @Context ContainerRequestContext context) {\n\n    if (timestamp < 0 || timestamp > MAX_TIMESTAMP) {\n      throw new BadRequestException(\"Illegal timestamp\");\n    }\n\n    if (multiRecipientMessage.getRecipients().isEmpty()) {\n      throw new BadRequestException(\"Recipient list is empty\");\n    }\n\n    final Timer.Sample sample = Timer.start();\n\n    try {\n      final SendMultiRecipientMessageResponse sendMultiRecipientMessageResponse;\n\n      if (isStory) {\n        if (groupSendToken != null) {\n          // Stories require no authentication. We fail requests that provide a groupSendToken, but for historical\n          // reasons we allow requests to set a combined access key, even though we ignore it\n          throw new BadRequestException(\"Group send token not allowed when sending stories\");\n        }\n\n        sendMultiRecipientMessageResponse =\n            sendMultiRecipientStoryMessage(multiRecipientMessage, timestamp, online, isUrgent, context);\n      } else {\n        sendMultiRecipientMessageResponse =","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java#L478-L514","documentation":"Thrown by sendMultiRecipientMessage when the recipient list resolved from the sealed-sender multi-recipient payload is empty. A multi-recipient message must name at least one recipient; a request whose decoded payload contains zero recipient entries fails this validation guard and is rejected with a 400-style client error.","triggerScenarios":"POST to the multi-recipient endpoint with a SealedSenderMultiRecipientMessage whose getRecipients() map is empty — i.e. the serialized payload contains no recipient entries.","commonSituations":"Client built the message from an empty distribution list or group with all members removed; recipient filtering (e.g. skipping unsent-to users) eliminated everyone; serialization bug dropping recipients.","solutions":["Check the recipient list client-side and skip the send (or surface an error) when it is empty.","Rebuild the multi-recipient message ensuring all intended recipients' entries are serialized.","Verify the distribution list/group actually has members at send time."],"exampleFix":"// before\nsendMultiRecipient(message); // recipients may be empty\n// after\nif (recipients.isEmpty()) { return; /* nothing to send */ }\nsendMultiRecipient(message);","handlingStrategy":"validation","validationCode":"if (!recipients || recipients.length === 0) return; // nothing to send, skip the API call","typeGuard":"function hasRecipients(recipients) { return Array.isArray(recipients) && recipients.length > 0; }","tryCatchPattern":"try { await sendMultiRecipient(msg); } catch (e) { if (e.status === 400 && /Recipient list is empty/.test(e.body)) { logSkipAndAbortSend(); } }","preventionTips":["Check group/distribution list membership before building the message","Handle the case where filtering removes all recipients","Test the empty-group edge case in client code"],"tags":["http-400","empty-list","validation"],"backgroundTag":"empty-required-field","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"}