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
- 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
- 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
- Add a round-trip unit test (serde/bcs serialize then deserialize_response) for the Response type to catch schema drift at build time
- 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
- Treat ApplicationIds as versioned artifacts: never hardcode them across upgrades, resolve via a registry you control
- Run a CI job that BCS-round-trips every cross-app Response type on both sides of the boundary
- Publish caller and callee from the same commit in your release pipeline
- Namespace or version application IDs in config so stale IDs fail loudly at startup, not mid-call
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
- Failed to deserialize event
- Failed to deserialize `Operation` in execute_operation
- Failed to deserialize message
- Failed to deserialize service response
- Application parameters must be deserializable
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/1667e6080335ab09.
Report an issue: GitHub.