{"record":{"id":"5e6905669607f026","repo":"signalapp/Signal-Server","slug":"delivery-cancelled","errorCode":null,"errorMessage":"delivery cancelled","messagePattern":"delivery cancelled","errorType":"http","errorClass":"InternalServerErrorException","httpStatus":500,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java","lineNumber":655,"sourceCode":"    spamChecker.checkForMultiRecipientSpamHttp(messageType, context).response().ifPresent(response -> {\n      throw new WebApplicationException(response);\n    });\n\n    try {\n      if (!resolvedRecipients.isEmpty()) {\n        messageSender.sendMultiRecipientMessage(multiRecipientMessage,\n            resolvedRecipients,\n            timestamp, isStory,\n            ephemeral,\n            urgent,\n            context.getHeaderString(HttpHeaders.USER_AGENT)).get();\n      }\n    } catch (final InterruptedException e) {\n      logger.error(\"interrupted while delivering multi-recipient messages\", e);\n      throw new InternalServerErrorException(\"interrupted during delivery\");\n    } catch (final CancellationException e) {\n      logger.error(\"cancelled while delivering multi-recipient messages\", e);\n      throw new InternalServerErrorException(\"delivery cancelled\");\n    } catch (final ExecutionException e) {\n      logger.error(\"partial failure while delivering multi-recipient messages\", e.getCause());\n      throw new InternalServerErrorException(\"failure during delivery\");\n    } catch (final MessageTooLargeException e) {\n      throw new WebApplicationException(Status.REQUEST_ENTITY_TOO_LARGE);\n    } catch (final MultiRecipientMismatchedDevicesException e) {\n      final List<AccountMismatchedDevices> accountMismatchedDevices =\n          e.getMismatchedDevicesByServiceIdentifier().entrySet().stream()\n              .filter(entry -> !entry.getValue().missingDeviceIds().isEmpty() || !entry.getValue().extraDeviceIds().isEmpty())\n              .map(entry -> new AccountMismatchedDevices(entry.getKey(),\n                  new MismatchedDevicesResponse(entry.getValue().missingDeviceIds(), entry.getValue().extraDeviceIds())))\n              .toList();\n\n      if (!accountMismatchedDevices.isEmpty()) {\n        throw new WebApplicationException(Response\n            .status(409)\n            .type(MediaType.APPLICATION_JSON_TYPE)\n            .entity(accountMismatchedDevices)","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java#L637-L673","documentation":"Thrown as HTTP 500 when one of the futures delivering a multi-recipient message is cancelled before completion. The server logs the cancellation and returns InternalServerErrorException with 'delivery cancelled'.","triggerScenarios":"Future.cancel() invoked on the per-recipient delivery futures, typically by a timeout or shutdown racing the delivery loop in sendMultiRecipientMessage.","commonSituations":"Server request timeout cancelling outstanding deliveries; executor shutdown during a large fan-out send.","solutions":["Retry the send; cancellation is usually server-side and transient","Increase client timeout to reduce the chance of server-side cancellation","Inspect server logs to find which timeout/cancellation policy triggered it"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await send(msg); } catch (e) { if (e.status === 500 && e.message === 'delivery cancelled') await withBackoff(() => send(msg)); else throw e; }","preventionTips":["Retry the send with exponential backoff","Keep request sizes moderate so delivery completes before server timeouts","Monitor server shutdown schedules when sending in bulk"],"tags":["http-500","cancellation","concurrency","signal-server"],"backgroundTag":"request-timeout","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"}