signalapp/Signal-Server · error · InternalServerErrorException

failure during delivery

Error message

failure during delivery

What it means

Thrown as HTTP 500 when at least one per-recipient delivery future in a multi-recipient send completes exceptionally (ExecutionException). The server logs the underlying cause and returns InternalServerErrorException 'failure during delivery'.

Solutions

  1. Retry the send; partial failures are often transient
  2. Check server logs for the logged underlying cause to identify the failing recipient
  3. If persistent, remove/re-resolve the problematic recipient's devices and resend
Defensive patterns

Strategy: retry

Try / catch

try { await send(msg); } catch (e) { if (e.status === 500 && e.message === 'failure during delivery') await withBackoff(() => send(msg)); else throw e; }

Prevention

When it happens

Trigger: One or more individual recipient message deliveries fail (storage/registration errors, downstream message-send exceptions) while the batch is being fanned out.

Common situations: A destination account in an inconsistent state; transient storage layer failure during a large story send.

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/90266364577de1fe. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java:658

    try {
      if (!resolvedRecipients.isEmpty()) {
        messageSender.sendMultiRecipientMessage(multiRecipientMessage,
            resolvedRecipients,
            timestamp, isStory,
            ephemeral,
            urgent,
            context.getHeaderString(HttpHeaders.USER_AGENT)).get();
      }
    } catch (final InterruptedException e) {
      logger.error("interrupted while delivering multi-recipient messages", e);
      throw new InternalServerErrorException("interrupted during delivery");
    } catch (final CancellationException e) {
      logger.error("cancelled while delivering multi-recipient messages", e);
      throw new InternalServerErrorException("delivery cancelled");
    } catch (final ExecutionException e) {
      logger.error("partial failure while delivering multi-recipient messages", e.getCause());
      throw new InternalServerErrorException("failure during delivery");
    } catch (final MessageTooLargeException e) {
      throw new WebApplicationException(Status.REQUEST_ENTITY_TOO_LARGE);
    } catch (final MultiRecipientMismatchedDevicesException e) {
      final List<AccountMismatchedDevices> accountMismatchedDevices =
          e.getMismatchedDevicesByServiceIdentifier().entrySet().stream()
              .filter(entry -> !entry.getValue().missingDeviceIds().isEmpty() || !entry.getValue().extraDeviceIds().isEmpty())
              .map(entry -> new AccountMismatchedDevices(entry.getKey(),
                  new MismatchedDevicesResponse(entry.getValue().missingDeviceIds(), entry.getValue().extraDeviceIds())))
              .toList();

      if (!accountMismatchedDevices.isEmpty()) {
        throw new WebApplicationException(Response
            .status(409)
            .type(MediaType.APPLICATION_JSON_TYPE)
            .entity(accountMismatchedDevices)
            .build());
      }

View on GitHub (pinned to 100ab61c82)