redis/jedis · error · IllegalArgumentException

BLOCK milliseconds must be a non-negative integer

Error message

BLOCK milliseconds must be a non-negative integer

What it means

TSReadParams.block(long milliseconds, int minCount) requires a non-negative BLOCK duration because Redis TS.MRANGE's BLOCK argument accepts only unsigned millisecond values; a negative value is meaningless and would produce a server error, so the client throws IllegalArgumentException immediately. 0 is allowed and means wait indefinitely.

Solutions

  1. Pass milliseconds >= 0; use 0 for indefinite blocking.
  2. Clamp computed deadlines: long ms = Math.max(0, deadline - System.currentTimeMillis());
  3. If 'do not wait' is intended, drop the block(...) call entirely rather than passing a negative value.

Example fix

// before
long ms = deadline - System.currentTimeMillis();
params.block(ms, 5); // throws if deadline passed
// after
long ms = Math.max(0, deadline - System.currentTimeMillis());
params.block(ms, 5);
Defensive patterns

Strategy: validation

Validate before calling

long ms = deadline - System.currentTimeMillis();
if (ms < 0) ms = 0; // or skip block()
params.block(ms, minCount);

Type guard

static boolean validBlockMs(long ms) { return ms >= 0; }

Try / catch

try { params.block(ms, minCount); } catch (IllegalArgumentException e) { params.block(0, minCount); }

Prevention

When it happens

Trigger: Calling TSReadParams.block(-1, 10), or computing the timeout as remaining-time (e.g. deadline - System.currentTimeMillis()) that has already gone negative.

Common situations: Timeout values derived from a deadline that expired before the call; configuration mistakes where the timeout was entered as a negative sentinel to mean 'no wait'.

Related errors


AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08). Data as JSON: /api/errors/5c27140faeb9197b. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/redis/clients/jedis/timeseries/TSReadParams.java:89

  /**
   * Cursor sentinel {@code $}: the latest sample's timestamp + 1, so only samples added after the
   * command is received qualify. Meaningful only together with {@link #block(long, int)}; without
   * blocking it always yields an empty reply.
   */
  public TSReadParams newSamples() {
    this.timestamp = DOLLAR;
    return this;
  }

  /**
   * Opt into blocking. Both values are always emitted on the wire inside the {@code BLOCK} group.
   * @param milliseconds maximum wait, non-negative; {@code 0} means wait indefinitely
   * @param minCount unblock threshold, positive; the call returns once this many samples qualify
   * @return this
   */
  public TSReadParams block(long milliseconds, int minCount) {
    if (milliseconds < 0) {
      throw new IllegalArgumentException("BLOCK milliseconds must be a non-negative integer");
    }
    if (minCount <= 0) {
      throw new IllegalArgumentException("BLOCK min_count must be a positive integer");
    }
    this.blockMilliseconds = milliseconds;
    this.blockMinCount = minCount;
    return this;
  }

  /**
   * Reply cap. When more samples qualify than {@code maxCount}, the oldest {@code maxCount} are
   * returned so callers can page forward. Omitted means unlimited.
   * @param maxCount positive integer
   * @return this
   */
  public TSReadParams maxCount(int maxCount) {
    if (maxCount <= 0) {
      throw new IllegalArgumentException("MAX_COUNT must be a positive integer");

View on GitHub (pinned to 6dac31d4c2)