redis/jedis · error · IllegalStateException
DISCARD without MULTI
Error message
DISCARD without MULTI
What it means
discard() cancels an open MULTI transaction by sending DISCARD. If no transaction is open (inMulti is false), the method throws IllegalStateException('DISCARD without MULTI') instead of sending a command the server would reject anyway. Like exec(), this is a client-side state machine check.
Solutions
- Only call discard() inside the same try block after multi() succeeded.
- Track transaction state (or use status()) and skip discard() when no MULTI is open.
- On failure before multi(), just abandon/close the transaction object without discarding.
Example fix
// before
Transaction t = jedis.multi(); // may fail before MULTI
try {
...
} finally {
t.discard(); // IllegalStateException if MULTI never ran
}
// after
Transaction t = jedis.multi();
try {
...
t.exec();
} catch (Exception e) {
t.discard();
throw e;
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!transactionOpened) { // your own flag set right after multi()
return; // nothing to discard
} Type guard
boolean canDiscard(Transaction t) { return t != null && t.status().isOpen(); } Try / catch
try {
transaction.discard();
} catch (IllegalStateException e) {
// no MULTI open: ignore, transaction already ended
} Prevention
- Set a boolean after multi() succeeds and only discard when it is true.
- Discard only inside the try block that follows a successful multi().
- Avoid unconditional discard in cleanup paths for transactions that may not have opened.
When it happens
Trigger: Calling discard() before multi() was ever called; calling discard() after a previous exec() or discard() already ended the transaction; calling discard() on a reset/cleared transaction object.
Common situations: Cleanup/finally blocks that unconditionally call discard() on a transaction whose multi() never ran (e.g. an earlier exception); double-release in retry logic; shared transaction instances across threads.
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
- EXEC without MULTI
- DISCARD command failed. Received response
- DISCARD without MULTI
- HashImport ' ' has been discarded
- Cannot use Jedis when in Multi. Please use Transaction or…
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/9dba5c5ce4b6a952.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/ReliableTransaction.java:199
} catch (JedisDataException e) {
formatted.add(e);
}
}
return formatted;
} catch (JedisConnectionException jce) {
broken = true;
throw jce;
} finally {
inMulti = false;
inWatch = false;
pipelinedResponses.clear();
}
}
@Override
public String discard() {
if (!inMulti) {
throw new IllegalStateException("DISCARD without MULTI");
}
try {
// processPipelinedResponses(pipelinedResponses.size());
// do nothing
connection.sendCommand(DISCARD);
String status = connection.getStatusCodeReply();
if (!"OK".equals(status)) {
throw new JedisException("DISCARD command failed. Received response: " + status);
}
return status;
} catch (JedisConnectionException jce) {
broken = true;
throw jce;
} finally {
inMulti = false;
inWatch = false;
pipelinedResponses.clear();View on GitHub (pinned to 6dac31d4c2)