{"record":{"id":"d8e4809cd04e0077","repo":"canopy-network/canopy","slug":"unexpected-handshake-response","errorCode":null,"errorMessage":"Unexpected handshake response","messagePattern":"Unexpected handshake response","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"plugin/kotlin/src/main/kotlin/com/canopy/plugin/PluginClient.kt","lineNumber":110,"sourceCode":"        logger.info { \"Handshaking with FSM\" }\n\n        val configMsg = PluginToFSM.newBuilder()\n            .setId(0)\n            .setConfig(ContractConfig.toPluginConfig())\n            .build()\n\n        val response = sendSync(null, configMsg)\n\n        when {\n            response.hasConfig() -> {\n                fsmConfig = response.config\n                logger.info { \"Handshake complete\" }\n            }\n            response.hasError() -> {\n                throw Exception(\"Handshake failed: ${response.error.msg}\")\n            }\n            else -> {\n                throw Exception(\"Unexpected handshake response\")\n            }\n        }\n    }\n\n    /**\n     * Read state from FSM\n     */\n    fun stateRead(contract: Contract, request: PluginStateReadRequest): PluginStateReadResponse {\n        val msg = PluginToFSM.newBuilder()\n            .setId(contract.fsmId)\n            .setStateRead(request)\n            .build()\n\n        val response = sendSync(contract, msg)\n\n        return when {\n            response.hasStateRead() -> response.stateRead\n            response.hasError() -> PluginStateReadResponse.newBuilder()","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/plugin/kotlin/src/main/kotlin/com/canopy/plugin/PluginClient.kt#L92-L128","documentation":"Thrown in PluginClient.handshake when the FSM's reply contains neither a config nor an error — an out-of-protocol response. It indicates a desynchronized or mismatched FSM (wrong message type, protocol drift, or corrupted transport) where the handshake cannot be interpreted.","triggerScenarios":"Thrown at plugin/kotlin/src/main/kotlin/com/canopy/plugin/PluginClient.kt:110 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Confirm plugin and FSM use the same proto message schema and protocol version.","Restart both plugin and FSM to re-establish a synchronized message sequence.","Inspect transport logging for dropped/garbled frames corrupting the response."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}