redis/jedis · error · JedisConnectionException

Unexpected end of stream.

Error message

Unexpected end of stream.

What it means

ensureFill refills the internal buffer from the socket; when read() returns -1 the stream has reached EOF, meaning the server (or an intermediary) closed the TCP connection while Jedis still expected reply bytes. Jedis wraps this in JedisConnectionException("Unexpected end of stream.").

Solutions

  1. Treat the connection as broken: close it and retry the command on a new connection.
  2. Configure pool validation (testWhileIdle, evictor) so dead connections are not handed out.
  3. Set socket timeout below server `timeout` and LB idle timeout; enable TCP keepalive.
  4. Inspect Redis server logs for shutdowns, OOM, or client kills at the failure timestamp.

Example fix

// before
GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
cfg.setTestOnBorrow(false);
// after
GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
cfg.setTestWhileIdle(true);
cfg.setTimeBetweenEvictionRunsMillis(30000);
cfg.setMinEvictableIdleTimeMillis(60000);
Defensive patterns

Strategy: retry

Try / catch

RetryTemplate-style:
for (int attempt = 0; attempt < 2; attempt++) {
  try (Jedis j = pool.getResource()) {
    return j.get(key);
  } catch (JedisConnectionException e) {
    if (attempt == 1) throw e;
    // else fall through and retry on a fresh connection
  }
}

Prevention

When it happens

Trigger: Thrown from ensureFill (used by peek, readByte, ensureCrLf, readLine, readLineBytes, readLineBytesSlowly) whenever more bytes are needed but the socket delivers EOF.

Common situations: Redis restarted or OOM-killed mid-command; server closed an idle connection (server `timeout`); LB/firewall silently dropping idle flows; command sent, then network drop before reply.

Related errors


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

Appendix: source

Thrown at src/main/java/redis/clients/jedis/util/RedisInputStream.java:259

    ensureFill();

    final int length = Math.min(limit - count, len);
    System.arraycopy(buf, count, b, off, length);
    count += length;
    return length;
  }

  /**
   * This method assumes there are required bytes to be read. If we cannot read anymore bytes an
   * exception is thrown to quickly ascertain that the stream was smaller than expected.
   */
  private void ensureFill() throws JedisConnectionException {
    if (count >= limit) {
      try {
        limit = in.read(buf);
        count = 0;
        if (limit == -1) {
          throw new JedisConnectionException("Unexpected end of stream.");
        }
      } catch (IOException e) {
        throw new JedisConnectionException(e);
      }
    }
  }

  @Override
  public int available() throws IOException {
    int availableInBuf = limit - count;
    int availableInSocket = this.in.available();
    return (availableInBuf > availableInSocket) ? availableInBuf : availableInSocket;
  }

}

View on GitHub (pinned to 6dac31d4c2)