{"record":{"id":"46dc3f27239f5de6","repo":"redis/jedis","slug":"active-database-has-changed-since-transaction-star","errorCode":null,"errorMessage":"Active database has changed since transaction started","messagePattern":"Active database has changed since transaction started","errorType":"exception","errorClass":"JedisException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/mcf/MultiDbTransaction.java","lineNumber":117,"sourceCode":"  @Override\n  public final String unwatch() {\n    Response<String> response = appendCommand(\n      new CommandObject<>(new CommandArguments(UNWATCH), BuilderFactory.STRING));\n    inWatch = false;\n    // when inside MULTI, the command has only been buffered; its reply will be delivered by exec()\n    return inMulti ? null : response.get();\n  }\n\n  @Override\n  protected final <T> Response<T> appendCommand(CommandObject<T> commandObject) {\n    if (inMulti) {\n      CommandArguments args = commandObject.getArguments();\n      Response<T> response = new Response<>(commandObject.getBuilder());\n      commands.add(KeyValue.of(args, response));\n      return response;\n    }\n    if (initialDatabase != null && !connectionSupplier.isActiveDatabase(initialDatabase)) {\n      throw new JedisException(\"Active database has changed since transaction started\");\n    }\n    try {\n      return Response.of(acquireConnection().executeCommand(commandObject));\n    } catch (JedisDataException e) {\n      return Response.error(e);\n    }\n  }\n\n  @Override\n  public void close() {\n    try {\n      if (inMulti) {\n        discard();\n      } else if (inWatch) {\n        unwatch();\n      }\n    } finally {\n      releaseConnection(false);","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/mcf/MultiDbTransaction.java#L99-L135","documentation":"MultiDbTransaction buffers commands locally. Pre-MULTI commands (e.g. WATCH) execute immediately against the database that was active when the transaction started (initialDatabase). appendCommand throws JedisException(\"Active database has changed since transaction started\") when a follow-up command arrives while the active database differs from initialDatabase, because commands would hit a different Redis instance and break transaction semantics.","triggerScenarios":"Calling any command (via status()/response() buffering path) on a MultiDbTransaction after a failover switched the active database, while the transaction had recorded an initialDatabase and is not yet in buffered MULTI mode.","commonSituations":"MultiDb failover triggered mid-transaction by health check failure; long-lived transaction objects spanning a database switch; tests or apps that call failoverToAnotherDatabase while a transaction is open.","solutions":["Complete (exec/discard) the transaction before triggering or waiting for failover","Catch JedisException and rebuild the transaction on the new active database (retry the whole WATCH/MULTI/EXEC cycle)","Check the active database (connectionSupplier.isActiveDatabase) before starting a transaction and keep transactions short"],"exampleFix":"// before\nTransaction t = client.transaction();\nt.watch(\"k\");\nfailover(); // active db changed\nt.set(\"k\", \"v\"); // throws\n// after\nTransaction t = client.transaction();\ntry {\n  t.watch(\"k\");\n  t.set(\"k\", \"v\");\n  t.exec();\n} catch (JedisException e) {\n  t.close();\n  t = client.transaction(); // retry on new active db\n}","handlingStrategy":"retry","validationCode":"if (!connectionSupplier.isActiveDatabase(initialDatabase)) { /* rebuild transaction on new active db */ }","typeGuard":null,"tryCatchPattern":"try { resp = t.set(key, value); } catch (JedisException e) { t.close(); t = client.transaction(); /* retry full WATCH/MULTI/EXEC */ }","preventionTips":["Keep transactions short so failover is unlikely mid-transaction","Avoid triggering failover while transactions are open","Retry the entire transaction (WATCH incl.) on active-database change"],"tags":["redis","failover","transactions","multi-db"],"backgroundTag":"invalid-state-transition","analyzedSha":"6dac31d4c224fb3257c216f3985340c6f500cdcb","analyzedAt":"2026-09-08T04:55:01.204Z","contentChangedAt":"2026-09-08T04:55:01.204Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}