{"record":{"id":"3b8c087ba4210b4f","repo":"apache/dubbo","slug":"cannot-be-started-once-stopped","errorCode":null,"errorMessage":"cannot be started once stopped","messagePattern":"cannot be started once stopped","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java","lineNumber":325,"sourceCode":"\n    /**\n     * Starts the background thread explicitly. The background thread will\n     * start automatically on demand even if you did not call this method.\n     *\n     * @throws IllegalStateException if this timer has been\n     * {@linkplain #stop() stopped} already\n     */\n    public void start() {\n        switch (WORKER_STATE_UPDATER.get(this)) {\n            case WORKER_STATE_INIT:\n                if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {\n                    workerThread.start();\n                }\n                break;\n            case WORKER_STATE_STARTED:\n                break;\n            case WORKER_STATE_SHUTDOWN:\n                throw new IllegalStateException(\"cannot be started once stopped\");\n            default:\n                throw new Error(\"Invalid WorkerState\");\n        }\n\n        // Wait until the startTime is initialized by the worker.\n        while (startTime == 0) {\n            try {\n                startTimeInitialized.await();\n            } catch (InterruptedException ignore) {\n                // Ignore - it will be ready very soon.\n            }\n        }\n    }\n\n    @Override\n    public Set<Timeout> stop() {\n        if (Thread.currentThread() == workerThread) {\n            throw new IllegalStateException(","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java#L307-L343","documentation":"IllegalStateException thrown by HashedWheelTimer.start() when the worker state is already WORKER_STATE_SHUTDOWN. Once a HashedWheelTimer is stopped (via stop() or GC/finalize), it cannot be restarted — the worker thread has terminated and internal state is torn down. start() is normally called lazily by newTimeout() or explicitly by the user; calling it on a stopped timer is a programming error.","triggerScenarios":"Calling start() (directly or indirectly via newTimeout()) on a HashedWheelTimer instance that has already had stop() called. This commonly happens when timer instances are reused or pooled and a stopped instance is accidentally used again.","commonSituations":"Reusing a HashedWheelTimer after calling stop(); framework shutdown that stops the shared timer followed by a late task scheduling attempt; connection/channel pools that cache and reuse timer references past their lifecycle; cleanup ordering where the timer is stopped before all users are quiesced.","solutions":["Do not reuse a HashedWheelTimer after stop() — create a new instance if timer functionality is needed again.","Ensure all components that reference the timer are stopped/unregistered before calling timer.stop().","Guard scheduling calls with timer.isStop() before calling newTimeout() or start().","Share a single long-lived HashedWheelTimer across the application (it is designed for this) rather than creating/stopping per-connection instances."],"exampleFix":"// before — reusing timer after stop\ntimer.stop();\n// ... later ...\ntimer.newTimeout(task, 5, TimeUnit.SECONDS); // calls start() internally -> throws\n\n// after — check lifecycle before use\nif (!timer.isStop()) {\n    timer.newTimeout(task, 5, TimeUnit.SECONDS);\n} else {\n    timer = new HashedWheelTimer(); // new instance\n    timer.newTimeout(task, 5, TimeUnit.SECONDS);\n}","handlingStrategy":"validation","validationCode":"if (!timer.isStop()) {\n    timer.start(); // or timer.newTimeout(...)\n} else {\n    // timer is stopped — create a new instance or skip\n    timer = new HashedWheelTimer();\n    timer.start();\n}","typeGuard":"public boolean isTimerUsable(HashedWheelTimer timer) {\n    return timer != null && !timer.isStop();\n}","tryCatchPattern":"try {\n    timer.newTimeout(task, delay, unit);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"cannot be started once stopped\")) {\n        logger.warn(\"Timer was stopped, replacing instance\");\n        timer = new HashedWheelTimer();\n        timer.newTimeout(task, delay, unit);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Never reuse a HashedWheelTimer after stop() — create a new instance.","Check timer.isStop() before scheduling new timeouts.","Share a single long-lived timer across the application; stop it only at final shutdown.","Track timer lifecycle with a reference holder that nulls out the reference after stop()."],"tags":["timer","lifecycle","shutdown","reuse"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}