linera-io/linera-protocol · critical

Failed to deserialize message

Error message

Failed to deserialize message

What it means

The contract macro's execute_message entrypoint (linera-sdk/src/contract/mod.rs) BCS-deserializes incoming message bytes into the contract's Message type and expects success. The panic fires when a cross-application/cross-chain message was serialized from a different Message schema than this contract declares - e.g. the sending application was upgraded but the receiving contract was not. The panic aborts message execution in the receiving contract.

Source

Thrown at linera-sdk/src/contract/mod.rs:81

                        let operation = <$contract as $crate::abi::ContractAbi>::deserialize_operation(operation)
                            .expect("Failed to deserialize `Operation` in execute_operation");

                        let response = contract.execute_operation(operation).blocking_wait();

                        <$contract as $crate::abi::ContractAbi>::serialize_response(response)
                            .expect("Failed to serialize `Response` in execute_operation")
                    },
                )
            }

            fn execute_message(message: Vec<u8>) {
                use $crate::util::BlockingWait as _;
                $crate::contract::run_async_entrypoint::<$contract, _, _>(
                    unsafe { &mut CONTRACT },
                    move |contract| {
                        let message: <$contract as $crate::Contract>::Message =
                            $crate::bcs::from_bytes(&message)
                                .expect("Failed to deserialize message");

                        contract.execute_message(message).blocking_wait()
                    },
                )
            }

            fn process_streams(updates: Vec<
                $crate::contract::wit::exports::linera::app::contract_entrypoints::StreamUpdate,
            >) {
                use $crate::util::BlockingWait as _;
                $crate::contract::run_async_entrypoint::<$contract, _, _>(
                    unsafe { &mut CONTRACT },
                    move |contract| {
                        let updates = updates.into_iter().map(Into::into).collect();
                        contract.process_streams(updates).blocking_wait()
                    },
                )
            }

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Redeploy both sender and receiver from the same shared crate so Message types match exactly.
  2. Treat any type change to Message as a new application version: publish anew rather than reusing the ID.
  3. Pin dApp component versions in the manifest so all modules deploy together.
  4. Round-trip test messages (bcs::to_bytes/from_bytes) in CI to catch schema drift before deploy.
Defensive patterns

Strategy: validation

Validate before calling

// Before sending a message to another application, verify both sides share the ABI:
use counter_app_abi::Message; // shared crate compiled into both applications
let bytes = bcs::to_bytes(&message)?;
bcs::from_bytes::<Message>(&bytes)?; // schema sanity check off-chain

Type guard

fn message_round_trips(msg: &Message) -> bool {
    bcs::to_bytes(msg).ok().and_then(|b| bcs::from_bytes::<Message>(&b).ok()).is_some()
}

Try / catch

// Deserialization happens inside the receiving contract's runtime; a panic fails the
// message execution on-chain and is not catchable by the sender. Prevent by validating
// ABI compatibility before dispatch (bytecode hash + shared crate version check).

Prevention

When it happens

Trigger: A message arrives from an application instance whose Message type differs from the receiving contract's: sender upgraded with changed message variants while the receiver runs old bytecode, or an application ID reused across incompatible versions.

Common situations: Inter-application messaging between two versions of the same dApp; a frontend composing messages with stale shared types; partial redeployment where only the sender was upgraded.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/82acfcce644f980c. Report an issue: GitHub.