{"record":{"id":"dba3127789c5626b","repo":"signalapp/Signal-Server","slug":"illegal-timestamp","errorCode":null,"errorMessage":"Illegal timestamp","messagePattern":"Illegal timestamp","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java","lineNumber":492,"sourceCode":"\n      @Parameter(description=\"If true, deliver the message only to recipients that are online when it is sent\")\n      @QueryParam(\"online\") boolean online,\n\n      @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","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/MessageController.java#L474-L510","documentation":"The multi-recipient (sealed sender) message endpoint validates the message timestamp against 0 <= timestamp <= MAX_TIMESTAMP; out-of-range values are rejected with a 400. This guards against negative or absurdly far-future timestamps from faulty or malicious clients.","triggerScenarios":"PUT/POST to the multi-recipient message endpoint with a timestamp query/body value that is negative or greater than the server's MAX_TIMESTAMP constant.","commonSituations":"Clock skew or clock going backwards on the client; timestamp passed in milliseconds where the API expects the other unit (or vice versa); uninitialized/0-minus values from a bug; integer overflow when computing the timestamp.","solutions":["Check the client's system clock and sync via NTP before sending.","Clamp/validate the timestamp client-side: reject if < 0 or greater than the protocol maximum before sending.","Confirm the timestamp unit (ms vs s) matches what the Signal protocol expects."],"exampleFix":"// before\nlong ts = System.currentTimeMillis() * 1000; // overflow-prone, may exceed MAX_TIMESTAMP\n// after\nlong ts = System.currentTimeMillis();\nif (ts < 0 || ts > MAX_TIMESTAMP) { throw new IllegalStateException(\"invalid timestamp: \" + ts); }","handlingStrategy":"validation","validationCode":"const ts = Date.now(); // ms\nif (ts < 0 || ts > MAX_TIMESTAMP) throw new Error(`timestamp out of range: ${ts}`);","typeGuard":"function isValidTimestamp(ts) { return typeof ts === 'number' && Number.isInteger(ts) && ts >= 0 && ts <= MAX_TIMESTAMP; }","tryCatchPattern":"try { await sendMultiRecipient(msg, ts); } catch (e) { if (e.status === 400 && /Illegal timestamp/.test(e.body)) { resyncClockAndRetryWithNewTimestamp(); } }","preventionTips":["Sync the device clock via NTP","Validate timestamp range client-side before every send","Use the protocol's timestamp unit consistently (milliseconds)"],"tags":["http-400","timestamp","validation"],"backgroundTag":"value-out-of-range","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"}