signalapp/Signal-Server · error · IllegalArgumentException

end of range must be after start of range

Error message

end of range must be after start of range

What it means

RedemptionRange.inclusive() validates a [start, end] instant range for receipt redemption. It throws IllegalArgumentException when redemptionStart is strictly after redemptionEnd, i.e. the caller supplied a backwards range.

Solutions

  1. Swap or re-order the arguments so start <= end before calling inclusive().
  2. Validate/normalize client-supplied dates before constructing the range.
  3. Use Instant.isAfter as a pre-check and return a 400 to the API caller.

Example fix

// before
RedemptionRange.inclusive(clock, endDate, startDate);
// after
if (startDate.isAfter(endDate)) {
  throw new BadRequestException("start must not be after end");
}
RedemptionRange.inclusive(clock, startDate, endDate);
Defensive patterns

Strategy: validation

Validate before calling

if (start.isAfter(end)) {
  throw new IllegalArgumentException("start must not be after end");
}

Type guard

boolean isValidRange(Instant start, Instant end) {
  return start != null && end != null && !start.isAfter(end);
}

Try / catch

try {
  RedemptionRange range = RedemptionRange.inclusive(clock, start, end);
} catch (IllegalArgumentException e) {
  return Response.status(400, e.getMessage()).build();
}

Prevention

When it happens

Trigger: Calling RedemptionRange.inclusive(clock, start, end) with start > end (even by milliseconds; equality is allowed).

Common situations: Swapped order of query parameters when computing a range from client input; timezone arithmetic producing an end earlier than start; off-by-one day when building ranges.

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


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

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/auth/RedemptionRange.java:55

  ///   - `redemptionEnd` >= `redemptionStart`
  ///   - `redemptionStart` and `redemptionEnd` are day-aligned
  ///   - `redemptionStart` is yesterday or later
  ///   - `redemptionEnd` is tomorrow + `MAX_REDEMPTION_DURATION` or earlier
  ///   - The number of days requested is less than `MAX_REDEMPTION_DURATION`
  ///
  /// @param clock           Clock to use to get current day
  /// @param redemptionStart The first day included in the range
  /// @param redemptionEnd   The last day included in the range
  /// @return A {@link RedemptionRange} that can be used to iterate each day between `redemptionStart` and
  ///  `redemptionEnd`
  /// @throws IllegalArgumentException if the redemption bounds were not valid
  public static RedemptionRange inclusive(Clock clock, Instant redemptionStart, Instant redemptionEnd)
      throws IllegalArgumentException {
    final Instant today = clock.instant().truncatedTo(ChronoUnit.DAYS);
    final Instant yesterday = today.minus(Duration.ofDays(1));

    if (redemptionStart.isAfter(redemptionEnd)) {
      throw new IllegalArgumentException("end of range must be after start of range");
    }

    if (!redemptionStart.truncatedTo(ChronoUnit.DAYS).equals(redemptionStart)
        || !redemptionEnd.truncatedTo(ChronoUnit.DAYS).equals(redemptionEnd)) {
      throw new IllegalArgumentException("timestamps must be day aligned");
    }

    if (redemptionStart.isBefore(yesterday)) {
      throw new IllegalArgumentException("start of range too far in the past");
    }

    if (redemptionEnd.isAfter(today.plus(MAX_REDEMPTION_DURATION).plus(Duration.ofDays(1)))) {
      throw new IllegalArgumentException("end of range too far in the future");
    }

    if (redemptionEnd.isAfter(redemptionStart.plus(MAX_REDEMPTION_DURATION))) {
      throw new IllegalArgumentException("redemption window too large");
    }

View on GitHub (pinned to 100ab61c82)