{"record":{"id":"5a7267bac6679f28","repo":"dotnet/aspnetcore","slug":"the-hubconnection-failed-to-transition-from-the","errorCode":null,"errorMessage":"The HubConnection failed to transition from the '%s' state to the '%s' state because it was actually in the '%s' state.","messagePattern":"The HubConnection failed to transition from the '(.+?)' state to the '(.+?)' state because it was actually in the '(.+?)' state\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java","lineNumber":1717,"sourceCode":"                }\n                return this.state;\n            } finally {\n                this.lock.unlock();\n            }\n        }\n\n        public HubConnectionState getHubConnectionState() {\n            return this.hubConnectionState;\n        }\n\n        public void changeState(HubConnectionState from, HubConnectionState to) {\n            this.lock.lock();\n            try {\n                logger.debug(\"The HubConnection is attempting to transition from the {} state to the {} state.\", from, to);\n                if (this.hubConnectionState != from) {\n                    logger.debug(\"The HubConnection failed to transition from the {} state to the {} state because it was actually in the {} state.\",\n                        from, to, this.hubConnectionState);\n                    throw new RuntimeException(String.format(\"The HubConnection failed to transition from the '%s' state to the '%s' state because it was actually in the '%s' state.\",\n                        from, to, this.hubConnectionState));\n                }\n\n                this.hubConnectionState = to;\n            } finally {\n                this.lock.unlock();\n            }\n        }\n\n        public void changeState(HubConnectionState to) {\n            this.lock.lock();\n            try {\n                logger.debug(\"The HubConnection is transitioning from the {} state to the {} state.\", this.hubConnectionState, to);\n                this.hubConnectionState = to;\n            } finally {\n                this.lock.unlock();\n            }\n        }","sourceCodeStart":1699,"sourceCodeEnd":1735,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java#L1699-L1735","documentation":"changeState(from, to) is a guarded state transition: it only succeeds if the connection is currently exactly in the 'from' state. If the actual state differs (because another thread already moved it, or the caller assumed the wrong starting state), it logs the mismatch and throws a formatted RuntimeException. This protects the state machine from illegal transitions.","triggerScenarios":"An internal or plugin code path calls changeState(CONNECTING, CONNECTED) when the state is actually DISCONNECTED (e.g. a late callback after stop()); or two reconnect attempts race and both try to transition from CONNECTING.","commonSituations":"Concurrent reconnect logic; a transport callback (onClose/onOpen) firing after the user called stop(); bugs in custom retry/reconnect wrappers layered on top of the client.","solutions":["Do not call the internal changeState directly; rely on start()/stop() and the built-in reconnect.","Serialize lifecycle calls - do not call start() and stop() concurrently or issue overlapping reconnects.","If wrapping reconnect, use a single-threaded executor and an AtomicReference<HubConnectionState> so transitions are owned by one thread."],"exampleFix":"// before - concurrent lifecycle\nnew Thread(() -> hub.start()).start();\nnew Thread(() -> hub.stop()).start();\n\n// after - serialized lifecycle\nScheduledExecutorService es = Executors.newSingleThreadExecutor();\nes.submit(() -> hub.start().blockingAwait());\nes.submit(() -> hub.stop().blockingAwait());","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Internal state-machine throw; surface as a connection failure and reconnect.\nhub.onClosed(ex -> {\n  if (ex != null && ex.getMessage().contains(\"failed to transition\")) {\n    log.warn(\"Illegal state transition; rebuilding connection\");\n    rebuildHubAndStart();\n  }\n});","preventionTips":["Never call start() and stop() concurrently; serialize lifecycle on one thread.","Do not stack custom reconnect logic that races with the client's own transitions.","Own all state transitions through a single-threaded executor."],"tags":["signalr","java","state-machine","concurrency","lifecycle"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}