{"record":{"id":"1667e6080335ab09","repo":"linera-io/linera-protocol","slug":"failed-to-deserialize-response-in-cross-applicat","errorCode":null,"errorMessage":"Failed to deserialize `Response` in cross-application call","messagePattern":"Failed to deserialize `Response` in cross-application call","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-sdk/src/contract/runtime.rs","lineNumber":307,"sourceCode":"\n    /// Calls another application.\n    pub fn call_application<A: ContractAbi + Send>(\n        &mut self,\n        authenticated: bool,\n        application: ApplicationId<A>,\n        call: &A::Operation,\n    ) -> A::Response {\n        let call_bytes = <A as ContractAbi>::serialize_operation(call)\n            .expect(\"Failed to serialize `Operation` in cross-application call\");\n\n        let response_bytes = contract_wit::try_call_application(\n            authenticated,\n            application.forget_abi().into(),\n            &call_bytes,\n        );\n\n        A::deserialize_response(response_bytes)\n            .expect(\"Failed to deserialize `Response` in cross-application call\")\n    }\n\n    /// Adds a new item to an event stream. Returns the new event's index in the stream.\n    pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {\n        contract_wit::emit(\n            &name.into(),\n            &bcs::to_bytes(value).expect(\"Failed to serialize event\"),\n        )\n    }\n\n    /// Reads an event from a stream. Returns the event's value.\n    ///\n    /// Fails the block if the event doesn't exist.\n    pub fn read_event(\n        &mut self,\n        chain_id: ChainId,\n        name: StreamName,\n        index: u32,","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-sdk/src/contract/runtime.rs#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: hardcoded ID from an older publish\nconst APP_ID: ApplicationId<FooAbi> = /* stored from v0.1 */;\nlet resp: FooResponse = runtime.call_application(true, APP_ID, query).await;\n\n// after: resolve the current module's application ID at startup and fail loudly on drift\nlet app_id = registry.latest_application_id::<FooAbi>().expect(\"foo app published\");\nassert_eq!(app_id.module_version().abi_hash, FooAbi::ABI_HASH, \"callee ABI drifted, republish caller\");\nlet resp: FooResponse = runtime.call_application(true, app_id, query).await;","handlingStrategy":"validation","validationCode":"// Before calling, pin the target app to the ABI you compiled against.\nfn abi_matches(expected: &str, candidate: &ApplicationId<FooAbi>) -> bool {\n    // compare a version/abi marker your registry exposes\n    registry.abi_hash(candidate).map(|h| h == expected).unwrap_or(false)\n}","typeGuard":"fn is_expected_foo_app(id: &ApplicationId<FooAbi>, expected_abi_hash: &str) -> bool {\n    registry::abi_hash(id).as_deref() == Some(expected_abi_hash)\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["linera","rust","sdk","cross-application-call","abi-mismatch","bcs"],"backgroundTag":"abi-mismatch","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}