redis/jedis · error · IllegalStateException

DISCARD without MULTI

Error message

DISCARD without MULTI

What it means

Thrown by Transaction.discard() when called outside an active MULTI block: inMulti is false because MULTI was never sent or the transaction was already executed/reset, so there is nothing for the server to DISCARD. It is a client-side state guard that avoids sending an invalid command sequence.

Solutions

  1. Only call discard() while the transaction is open (after multi(), before exec()).
  2. Guard cleanup code with a flag or check before discarding.
  3. If the block was already exec'd, discard is unnecessary — skip it.
  4. Create a new Transaction if a fresh block is needed.

Example fix

// before
Transaction t = jedis.transaction(false);
t.discard(); // throws: never in MULTI
// after
Transaction t = jedis.transaction(false);
t.multi();
t.set("k", "v");
t.discard();
Defensive patterns

Strategy: validation

Validate before calling

boolean blockOpen = startedMulti && !executed;
if (blockOpen) tx.discard();

Try / catch

try {
  tx.discard();
} catch (IllegalStateException e) {
  // block already closed; safe to ignore
}

Prevention

When it happens

Trigger: Calling discard() on a Transaction created with doMulti=true before any commands (MULTI already implicit? no — discard before multi in manual mode), calling discard() twice, or calling discard() after exec() already closed the block.

Common situations: Cleanup/finally blocks that unconditionally call discard(); error-handling paths where the transaction never entered MULTI; double abort attempts.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/main/java/redis/clients/jedis/Transaction.java:224

          formatted.add(e);
        }
      }
      return formatted;
    } catch (JedisConnectionException jce) {
      broken = true;
      throw jce;
    } finally {
      inMulti = false;
      inWatch = false;
      pipelinedResponses.clear();
      onAfterExec();
    }
  }

  @Override
  public String discard() {
    if (!inMulti) {
      throw new IllegalStateException("DISCARD without MULTI");
    }

    try {
      // ignore QUEUED (or ERROR)
      // processPipelinedResponses(pipelinedResponses.size());
      connection.getMany(1 + pipelinedResponses.size());

      connection.sendCommand(DISCARD);

      return connection.getStatusCodeReply();
    } catch (JedisConnectionException jce) {
      broken = true;
      throw jce;
    } finally {
      inMulti = false;
      inWatch = false;
      pipelinedResponses.clear();
      onAfterDiscard();

View on GitHub (pinned to 6dac31d4c2)