linera-io/linera-protocol · error

Failed to deserialize `Response` in cross-application call

Error message

Failed to deserialize `Response` in cross-application call

What it means

This panic fires inside ContractRuntime::call_application after the host returns the callee application's response bytes and A::deserialize_response fails to decode them. It means the caller's compiled-in expectation of the target application's response type does not match what the deployed callee actually returned. This is almost always an ABI or application-version mismatch, not a transient failure.

Source

Thrown at linera-sdk/src/contract/runtime.rs:307

    /// Calls another application.
    pub fn call_application<A: ContractAbi + Send>(
        &mut self,
        authenticated: bool,
        application: ApplicationId<A>,
        call: &A::Operation,
    ) -> A::Response {
        let call_bytes = <A as ContractAbi>::serialize_operation(call)
            .expect("Failed to serialize `Operation` in cross-application call");

        let response_bytes = contract_wit::try_call_application(
            authenticated,
            application.forget_abi().into(),
            &call_bytes,
        );

        A::deserialize_response(response_bytes)
            .expect("Failed to deserialize `Response` in cross-application call")
    }

    /// Adds a new item to an event stream. Returns the new event's index in the stream.
    pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
        contract_wit::emit(
            &name.into(),
            &bcs::to_bytes(value).expect("Failed to serialize event"),
        )
    }

    /// Reads an event from a stream. Returns the event's value.
    ///
    /// Fails the block if the event doesn't exist.
    pub fn read_event(
        &mut self,
        chain_id: ChainId,
        name: StreamName,
        index: u32,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Re-derive the target ApplicationId from the exact bytecode/module version the caller was compiled against, and pass that ID (e.g. via the application registry) instead of a stale hardcoded one
  2. Rebuild and republish BOTH caller and callee from the same commit/crate version after any change to the callee's Abi Response type, then update the stored ApplicationId
  3. Add a round-trip unit test (serde/bcs serialize then deserialize_response) for the Response type to catch schema drift at build time
  4. Check the callee's Response derives (Serialize/Deserialize with the same format the SDK uses) and that no custom serde attribute silently changes the encoding

Example fix

// before: hardcoded ID from an older publish
const APP_ID: ApplicationId<FooAbi> = /* stored from v0.1 */;
let resp: FooResponse = runtime.call_application(true, APP_ID, query).await;

// after: resolve the current module's application ID at startup and fail loudly on drift
let app_id = registry.latest_application_id::<FooAbi>().expect("foo app published");
assert_eq!(app_id.module_version().abi_hash, FooAbi::ABI_HASH, "callee ABI drifted, republish caller");
let resp: FooResponse = runtime.call_application(true, app_id, query).await;
Defensive patterns

Strategy: validation

Validate before calling

// Before calling, pin the target app to the ABI you compiled against.
fn abi_matches(expected: &str, candidate: &ApplicationId<FooAbi>) -> bool {
    // compare a version/abi marker your registry exposes
    registry.abi_hash(candidate).map(|h| h == expected).unwrap_or(false)
}

Type guard

fn is_expected_foo_app(id: &ApplicationId<FooAbi>, expected_abi_hash: &str) -> bool {
    registry::abi_hash(id).as_deref() == Some(expected_abi_hash)
}

Prevention

When it happens

Trigger: Calling runtime.call_application(authenticated, application_id, argument) where application_id points to a module published from a different version of the callee crate whose Response type changed (added/removed/renamed fields), or where the ApplicationId was constructed for the wrong application entirely.

Common situations: Re-publishing an upgraded bytecode and calling it with an old caller module (or vice versa); hardcoding an ApplicationId from another network/genesis; mixing Wasm and EVM application IDs; a Response type whose serde attributes (e.g. deny_unknown_fields, tag) changed between versions.

Related errors


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