{"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/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java#L1699-L1735","documentation":"Thrown by the internal changeState(from, to) guard when the connection's actual HubConnectionState does not equal the expected 'from' state. It signals a state-machine inconsistency, almost always caused by concurrent or re-entrant start()/stop()/automatic-reconnect operations racing on the same HubConnection. The start() error handler at HubConnection.java:350-353 intentionally swallows it, so it normally surfaces only outside that guarded path.","triggerScenarios":"Calling start() while a previous start() is still mid-handshake; calling stop() during the CONNECTING->CONNECTED transition at line 324; withAutomaticReconnect racing a manual stop(); invoking start() twice in quick succession without waiting on the first Completable.","commonSituations":"Background reconnect logic overlapping a manual reconnect; a UI 'Connect' button that allows double-tap; withAutomaticReconnect enabled combined with manual stop/start cycling; reusing a HubConnection instance across rapid lifecycle changes.","solutions":["Serialize lifecycle calls: block on the previous start()/stop() Completable before issuing the next.","Guard with hubConnection.getHubConnectionState() before calling start()/stop().","Coordinate/disable withAutomaticReconnect when you manually manage connection lifecycle.","Avoid issuing start() on an instance that is already CONNECTING/CONNECTED."],"exampleFix":"// before\nhubConnection.start().subscribe(() -> {}, e -> {});\nhubConnection.start().subscribe(() -> {}, e -> {}); // races\n\n// after\nhubConnection.start().blockingAwait();\n// only start again after stop().blockingAwait()","handlingStrategy":"validation","validationCode":"// Java - check state before lifecycle calls\nif (hubConnection.getHubConnectionState() == HubConnectionState.DISCONNECTED) {\n    hubConnection.start().blockingAwait();\n}","typeGuard":null,"tryCatchPattern":"// Wrap a contested lifecycle op; this error is usually swallowed internally,\n// so prefer preventing the race over catching it.\ntry {\n    hubConnection.start().blockingAwait();\n} catch (RuntimeException ex) {\n    // log and reconcile state before retrying\n}","preventionTips":["Never call start() concurrently with another start()/stop()/reconnect on the same instance.","Always block on the previous start()/stop() Completable before issuing the next.","If using withAutomaticReconnect, do not also manually start/stop in a way that overlaps it."],"tags":["signalr","state-machine","concurrency","java"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}