signalapp/Signal-Server · error · BadRequestException
Group send token not allowed when sending stories
Error message
Group send token not allowed when sending stories
What it means
Stories on the multi-recipient path require no authentication, so providing a group send token header with a story send is rejected with a 400. (For historical reasons a combined unidentified-access key is tolerated but ignored.)
Solutions
- Do not attach the group send token header when the message is a story.
- Route story sends through the story-specific path without endorsement headers.
- Audit client request-building code to conditionally set the token based on message type.
Example fix
// before
builder.header("X-Group-Send-Token", token);
sendMultiRecipient(payload, /* isStory */ true);
// after
if (!isStory) { builder.header("X-Group-Send-Token", token); }
sendMultiRecipient(payload, isStory); Defensive patterns
Strategy: validation
Validate before calling
if (isStory && headers["X-Group-Send-Token"]) { delete headers["X-Group-Send-Token"]; } Try / catch
try { await sendMultiRecipient(payload, isStory); } catch (e) { if (e.status === 400 && /not allowed when sending stories/.test(e.body)) { resendWithoutToken(); } } Prevention
- Only attach group send tokens for non-story sends
- Keep story sends on a dedicated request path
- Cover story sends in integration tests to catch header leakage
When it happens
Trigger: POST to the multi-recipient endpoint with the story flag set and the group send endorsement token header present.
Common situations: Client attaches group send tokens to every multi-recipient send including stories; story support added to a code path that always sets the token header; reused request builders.
Related errors
- Group send endorsement tokens should not be sent for story…
- Group send endorsement tokens should not be combined with…
- Only one of group send endorsement token and unidentified…
- Invalid length
- Invalid create call link credential request
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/c986a2a8a0939e02.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java:508
if (timestamp < 0 || timestamp > MAX_TIMESTAMP) {
throw new BadRequestException("Illegal timestamp");
}
if (multiRecipientMessage.getRecipients().isEmpty()) {
throw new BadRequestException("Recipient list is empty");
}
final Timer.Sample sample = Timer.start();
try {
final SendMultiRecipientMessageResponse sendMultiRecipientMessageResponse;
if (isStory) {
if (groupSendToken != null) {
// Stories require no authentication. We fail requests that provide a groupSendToken, but for historical
// reasons we allow requests to set a combined access key, even though we ignore it
throw new BadRequestException("Group send token not allowed when sending stories");
}
sendMultiRecipientMessageResponse =
sendMultiRecipientStoryMessage(multiRecipientMessage, timestamp, online, isUrgent, context);
} else {
sendMultiRecipientMessageResponse =
sendMultiRecipientMessage(multiRecipientMessage, timestamp, online, isUrgent, groupSendToken, accessKeys,
context);
}
return Response.ok(sendMultiRecipientMessageResponse).build();
} finally {
sample.stop(MULTI_RECIPIENT_MESSAGE_LATENCY_TIMER);
}
}
private SendMultiRecipientMessageResponse sendMultiRecipientMessage(final SealedSenderMultiRecipientMessage multiRecipientMessage,
final long timestamp,View on GitHub (pinned to 100ab61c82)