{"record":{"id":"183451f5a0f39132","repo":"linera-io/linera-protocol","slug":"failed-to-deserialize-query-response-from-applicat","errorCode":null,"errorMessage":"Failed to deserialize query response from application","messagePattern":"Failed to deserialize query response from application","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-sdk/src/service/runtime.rs","lineNumber":218,"sourceCode":"            .expect(\"Failed to serialize application operation\");\n\n        service_wit::schedule_operation(&bytes);\n    }\n\n    /// Queries another application.\n    pub fn query_application<A: ServiceAbi>(\n        &self,\n        application: ApplicationId<A>,\n        query: &A::Query,\n    ) -> A::QueryResponse {\n        let query_bytes =\n            serde_json::to_vec(&query).expect(\"Failed to serialize query to another application\");\n\n        let response_bytes =\n            service_wit::try_query_application(application.forget_abi().into(), &query_bytes);\n\n        serde_json::from_slice(&response_bytes)\n            .expect(\"Failed to deserialize query response from application\")\n    }\n}\n\nimpl<Application> ServiceRuntime<Application>\nwhere\n    Application: Service,\n{\n    /// Loads a value from the `slot` cache or fetches it and stores it in the cache.\n    fn fetch_value_through_cache<T>(slot: &Mutex<Option<T>>, fetch: impl FnOnce() -> T) -> T\n    where\n        T: Clone,\n    {\n        let mut value = slot\n            .lock()\n            .expect(\"Mutex should never be poisoned because service runs in a single thread\");\n\n        if value.is_none() {\n            *value = Some(fetch());","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-sdk/src/service/runtime.rs#L200-L236","documentation":"ServiceRuntime::query_application forwards a JSON query to another application through the host (service_wit::try_query_application) and parses the returned bytes with serde_json::from_slice::<A::QueryResponse>. The expect panics when the target application's query response does not decode into the type the caller compiled against.","triggerScenarios":"Calling query_application::<OtherApp>(application_id, query) where the deployed OtherApp returns JSON not matching the caller's QueryResponse — version drift between modules, wrong application_id, or a response schema change (field rename, nesting, Option/required flip).","commonSituations":"Running a proxy/composed service (e.g. a wrapper app querying a token app) after the inner app was upgraded to a new response format; querying an application whose query handler errors and returns an error body instead of the expected payload; mixing module versions across testnet resets.","solutions":["Align versions: rebuild the querying service against the exact crate version of the target application that is deployed, then restart the service","Validate the raw response first during debugging: log serde_json::from_slice::<serde_json::Value>(&response_bytes) to see the actual shape the peer returned","Make the caller's QueryResponse tolerant (Option fields, #[serde(default)], ignore unknown keys) for forward compatibility","Check that application_id comes from the same publish/genesis as the deployed target, not from a config file of a previous network"],"exampleFix":"// before: panic on any drift\nlet res: TokenResponse = runtime.query_application(app_id, q).await;\n\n// after (debug path): inspect then decode with a clear message\nlet bytes = ...; // response bytes\nlet value: serde_json::Value = serde_json::from_slice(&bytes)\n    .expect(\"peer returned non-JSON\");\nlet res: TokenResponse = serde_json::from_value(value)\n    .unwrap_or_else(|e| panic!(\"token app response schema mismatch: {e}\"));","handlingStrategy":"validation","validationCode":"// Decode defensively via serde_json::Value first when peers are versioned.\nlet v: serde_json::Value = serde_json::from_slice(&response_bytes).map_err(QueryError::BadJson)?;\nlet r: A::QueryResponse = serde_json::from_value(v).map_err(QueryError::SchemaMismatch)?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pin peer application crates to exact versions and republish together","Keep an integration test that queries a locally-instantiated peer service and asserts the decoded QueryResponse","Design QueryResponse additively: new fields optional, old fields never renamed","Surface schema-mismatch errors as application errors instead of letting the runtime panic take down the node service"],"tags":["linera","rust","sdk","json","serde","cross-application-query"],"backgroundTag":"json-deserialization-error","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}