signalapp/Signal-Server · error · WebApplicationException
Multi-recipient messages must be addressed to ACI service…
Error message
Multi-recipient messages must be addressed to ACI service IDs
What it means
Multi-recipient messages may only be addressed to ACI (account identity) service IDs; PNIs (phone-number identities) are rejected. checkAccessKeys returns HTTP 401 with this message when any recipient in the payload uses a PNI.
Solutions
- Address every recipient by their ACI (UUID), not their PNI
- Look up the ACI from the contact/profile instead of deriving the service ID from the phone number
- Validate the recipient list client-side and strip/filter PNI entries before sending
Example fix
// before
if (serviceId instanceof ServiceId.Pni) { /* add recipient */ }
// after
if (serviceId instanceof ServiceId.Aci) { /* add recipient */ } Defensive patterns
Strategy: validation
Validate before calling
function allAci(serviceIds) {
return serviceIds.every(id => id.startsWith('00000000-0000-0000-0000-0000000000') === false && isAciUuid(id));
}
// or: verify ids came from ACI (identity key) lookups, not PNI (phone-number) lookups Type guard
function isAci(serviceId) { return serviceId.type === 'ACI'; } Try / catch
try { await send(msg); } catch (e) { if (e.status === 401 && /ACI service IDs/.test(e.message)) readdressToAciAndRetry(msg); else throw e; } Prevention
- Always address sealed-sender multi-recipient messages by ACI
- Keep ACI and PNI lookups in clearly separate code paths
- Validate the recipient list before serializing the payload
When it happens
Trigger: Including a ServiceId.Pni in the recipient set of a POST to the multi-recipient message endpoint.
Common situations: Client resolving contacts by phone number and using the PNI instead of the ACI when building a sealed-sender story send.
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
- access key length must be 16
- Invalid combined unidentified sender access keys
- Only primary devices may register attestations
- Invalid action:
- A group send endorsement token or unidentified access key…
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/68814edf9ef16024.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java:716
private void checkGroupSendToken(final Collection<ServiceId> recipients, final GroupSendFullToken groupSendFullToken) {
try {
groupSendFullToken.verify(recipients,
clock.instant(),
GroupSendDerivedKeyPair.forExpiration(groupSendFullToken.getExpiration(), serverSecretParams));
} catch (final VerificationFailedException e) {
throw new NotAuthorizedException(e);
}
}
private void checkAccessKeys(
final @NotNull CombinedUnidentifiedSenderAccessKeys accessKeys,
final SealedSenderMultiRecipientMessage multiRecipientMessage,
final Map<SealedSenderMultiRecipientMessage.Recipient, Account> resolvedRecipients) {
if (multiRecipientMessage.getRecipients().keySet().stream()
.anyMatch(serviceId -> serviceId instanceof ServiceId.Pni)) {
throw new WebApplicationException("Multi-recipient messages must be addressed to ACI service IDs",
Status.UNAUTHORIZED);
}
try {
if (!UnidentifiedAccessUtil.checkUnidentifiedAccess(resolvedRecipients.values(), accessKeys.getAccessKeys())) {
throw new WebApplicationException(Status.UNAUTHORIZED);
}
} catch (final IllegalArgumentException ignored) {
throw new WebApplicationException(Status.UNAUTHORIZED);
}
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/report/{source}/{messageGuid}")
public Response reportSpamMessage(
@Auth AuthenticatedDevice auth,
@PathParam("source") String source,View on GitHub (pinned to 100ab61c82)