redis/jedis · error · RejectedExecutionException
controller closed
Error message
controller closed
What it means
MaintenanceEventController lazily creates its ScheduledExecutorService. If scheduling is requested after the controller was closed, it throws RejectedExecutionException("controller closed") rather than resurrecting a scheduler on a closed controller.
Solutions
- Stop using the controller/client after close(); check the closed state before scheduling
- Ensure push consumers and background tasks are cancelled before closing the controller
- Create a new client/controller instance if you need to continue scheduling after a close
Example fix
// before
controller.close();
controller.schedulePolling(...); // RejectedExecutionException
// after
if (!controller.isClosed()) {
controller.schedulePolling(...);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (controller == null || controller.isClosed()) {
throw new IllegalStateException("Controller already closed");
} Try / catch
try {
controller.schedule(task, delay, unit);
} catch (RejectedExecutionException e) {
logger.debug("Controller closed; skipping scheduling", e);
} Prevention
- Cancel push consumers and background jobs before closing the controller/client
- Never reuse a client instance after close(); create a new one
- Make shutdown ordering explicit: stop producers of scheduling requests first
When it happens
Trigger: Calling any controller method that schedules maintenance polling/rebind work after close() has been called on the controller (or after the owning client/pool was closed and shut the controller down).
Common situations: Background threads or push-event handlers still referencing the controller after the client/pool was closed; a race where a scheduled task fires during shutdown and tries to reschedule; reusing a closed client.
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
- null is not a valid argument.
- Failed to create socket.
- HashImport ' ' has been discarded
- HashImport ' ' expects values but got
- 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/897ec98e9067122e.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/MaintenanceEventController.java:84
/**
* Test seam: an explicit marking scheduler (pre-populates the lazy field). The controller owns it
* and shuts it down on {@link #close()}.
*/
static MaintenanceEventController from(MaintenanceNotificationsConfig cfg,
ScheduledExecutorService scheduler) {
return new MaintenanceEventController(cfg, scheduler);
}
/**
* The marking scheduler, created on first use.
*/
private ScheduledExecutorService scheduler() {
ScheduledExecutorService s = scheduler;
if (s == null) {
synchronized (schedulerLock) {
if (closed) {
throw new RejectedExecutionException("controller closed");
}
s = scheduler;
if (s == null) {
scheduler = s = newMaintenanceScheduler();
}
}
}
return s;
}
private static final AtomicInteger MAINTENANCE_THREAD_SEQ = new AtomicInteger();
private static ScheduledExecutorService newMaintenanceScheduler() {
String name = "jedis-maintenance-" + MAINTENANCE_THREAD_SEQ.incrementAndGet();
return Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, name);
t.setDaemon(true);
return t;View on GitHub (pinned to 6dac31d4c2)