redis/jedis · error · IllegalStateException

HashImport ' ' has been discarded

Error message

HashImport '<name>' has been discarded

What it means

HashImportSupport.checkArgs validates a HashImport (a Redis hash-field import fieldset) before use. It throws IllegalStateException when the fieldset has already been discarded (e.g. after a failed prepare or explicit discard), since using it afterward would send stale or invalid state to the server. The name in the message identifies which fieldset was misused.

Solutions

  1. Create a new HashImport fieldset instance instead of reusing the discarded one
  2. Check fieldset.isDiscarded() before each import call and rebuild if true
  3. Fix the root cause that led to the discard (usually a failed prepare/connection error) before retrying

Example fix

// before
HashImport fieldset = buildFieldset();
try {
  jedis.himport(namespace, key, fieldset, values);
} catch (JedisException e) {
  jedis.himport(namespace, key, fieldset, values); // IllegalStateException: discarded
}
// after
HashImport fieldset = buildFieldset();
try {
  jedis.himport(namespace, key, fieldset, values);
} catch (JedisException e) {
  HashImport fresh = buildFieldset();
  jedis.himport(namespace, key, fresh, values);
}
Defensive patterns

Strategy: validation

Validate before calling

if (fieldset == null || fieldset.isDiscarded()) {
  fieldset = rebuildFieldset();
}

Type guard

boolean usable(HashImport fs) { return fs != null && !fs.isDiscarded(); }

Try / catch

try {
  hashImportCall(fieldset, values);
} catch (IllegalStateException e) {
  if (e.getMessage().contains("discarded")) {
    fieldset = rebuildFieldset();
    hashImportCall(fieldset, values);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling a hash-import command method with a HashImport fieldset after discard() was called on it, or after a failed prepare(Connection) left it discarded. checkArgs is invoked as the first step of every fieldset-consuming API call.

Common situations: Reusing a fieldset across two import calls where the first failed mid-way; catching an exception from a previous import and retrying with the same, now-discarded, HashImport object; long-lived fieldset objects held in application state that were discarded during a connection reset.

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/5e9ec263d24ca871. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/redis/clients/jedis/HashImportSupport.java:19

package redis.clients.jedis;

/**
 * {@code HIMPORT SET} helpers: client-side argument validation, and the per-connection
 * prepare-before-use that a {@code himportSet} {@link CommandObject}'s
 * {@linkplain CommandObject#getPreProcessHooks() pre-process hook} runs. The hook is invoked by
 * {@link Connection#executeCommand(CommandObject)} once the {@code CommandExecutor} has picked a
 * connection &mdash; so retry / cluster redirection / failover stay in force &mdash; injecting a
 * {@code PREPARE} on that connection just before the {@code SET} when the fieldset is not yet
 * prepared there.
 */
final class HashImportSupport {

  private HashImportSupport() {
  }

  static void checkArgs(HashImport fieldset, int valueCount) {
    if (fieldset.isDiscarded()) {
      throw new IllegalStateException("HashImport '" + fieldset.name() + "' has been discarded");
    }
    if (valueCount != fieldset.size()) {
      throw new IllegalArgumentException("HashImport '" + fieldset.name() + "' expects "
          + fieldset.size() + " values but got " + valueCount);
    }
  }

  /**
   * Prepare-before-use: if {@code fieldset} is not yet prepared on {@code connection}, send
   * {@code HIMPORT PREPARE} and record it in the connection's note. The PREPARE is built here, only
   * when actually needed &mdash; the common already-prepared case allocates nothing. The caller
   * must own the connection.
   */
  static void prepareBeforeUse(Connection connection, HashImport fieldset) {
    if (!connection.himportState().isPrepared(fieldset.name())) {
      connection.executeCommand(new CommandArguments(Protocol.Command.HIMPORT)
          .add(Protocol.Keyword.PREPARE).add(fieldset.name()).addObjects(fieldset.fields()));
      markPrepared(connection, fieldset);

View on GitHub (pinned to 6dac31d4c2)