canopy-network/canopy · error · kotlin.Exception

Unexpected state write response

Error message

Unexpected state write response

What it means

Thrown in PluginClient.stateWrite when the FSM's response to a state-write request contains neither a stateWrite result nor an error — an unrecognized reply shape. This leaves the write's outcome unknown and indicates protocol desynchronization rather than a rejected write (rejections arrive as an error field).

Source

Thrown at plugin/kotlin/src/main/kotlin/com/canopy/plugin/PluginClient.kt:151

    }

    /**
     * Write state to FSM
     */
    fun stateWrite(contract: Contract, request: PluginStateWriteRequest): PluginStateWriteResponse {
        val msg = PluginToFSM.newBuilder()
            .setId(contract.fsmId)
            .setStateWrite(request)
            .build()

        val response = sendSync(contract, msg)

        return when {
            response.hasStateWrite() -> response.stateWrite
            response.hasError() -> PluginStateWriteResponse.newBuilder()
                .setError(response.error)
                .build()
            else -> throw Exception("Unexpected state write response")
        }
    }

    /**
     * Execute a detached, read-only state query against Canopy at the given height (0 = latest committed).
     *
     * Unlike [stateRead], it is NOT tied to an in-flight tx/block lifecycle and does not require a
     * Contract context; it allocates its own fresh RANDOM request id, making it safe to call from
     * custom RPC handlers (e.g. the plugin's own HTTP server).
     */
    fun queryState(height: Long, request: PluginStateReadRequest): PluginStateReadResponse {
        // generate a fresh random request id (not tied to any in-flight FSM request)
        val requestId = Random.nextLong()
        val msg = PluginToFSM.newBuilder()
            .setId(requestId)
            .setQuery(
                PluginQueryRequest.newBuilder()
                    .setHeight(height)

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Verify request id tracking is correct so the pending response matches the state-write request.
  2. Ensure matching proto schemas between plugin and FSM.
  3. Restart the connection/plugin to resynchronize and reissue the write.
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at plugin/kotlin/src/main/kotlin/com/canopy/plugin/PluginClient.kt:151 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/25fd66dc23068b5b. Report an issue: GitHub.