{"record":{"id":"3d9cd8fa10b6c112","repo":"linera-io/linera-protocol","slug":"attempt-to-modify-storage-from-a-service","errorCode":null,"errorMessage":"Attempt to modify storage from a service","messagePattern":"Attempt to modify storage from a service","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-sdk/src/views/system_api.rs","lineNumber":343,"sourceCode":"            WitInterface::Service => service_wit::find_key_values_wait(promise),\n            #[cfg(with_testing)]\n            WitInterface::Mock { store, .. } => store.find_key_values_wait(promise),\n        }\n    }\n\n    /// Calls the `write_batch` WIT function.\n    fn write_batch(&self, batch: Batch) {\n        match self {\n            WitInterface::Contract => {\n                let batch_operations = batch\n                    .operations\n                    .into_iter()\n                    .map(WriteOperation::from)\n                    .collect::<Vec<_>>();\n\n                contract_runtime_api::write_batch(&batch_operations);\n            }\n            WitInterface::Service => panic!(\"Attempt to modify storage from a service\"),\n            #[cfg(with_testing)]\n            WitInterface::Mock {\n                store,\n                read_only: false,\n            } => {\n                store.write_batch(batch);\n            }\n            #[cfg(with_testing)]\n            WitInterface::Mock {\n                read_only: true, ..\n            } => {\n                panic!(\"Attempt to modify storage from a service\")\n            }\n        }\n    }\n}\n\n/// Implementation of [`linera_views::context::Context`] to be used for data storage","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-sdk/src/views/system_api.rs#L325-L361","documentation":"linera-sdk's system API for views dispatches write_batch differently per runtime interface. Contract runtimes forward batches to the host to mutate chain state; service runtimes are read-only by protocol design, so write_batch on WitInterface::Service panics with 'Attempt to modify storage from a service'. Any state mutation must go through contract operations inside blocks.","triggerScenarios":"Inside a service's handle_query (or any service-side code), calling a view method that persists: view.save().await, ViewStorage write operations, queue pushes — anything that eventually calls write_batch on the service interface.","commonSituations":"Porting a contract method into the service; forgetting that services answer queries without creating blocks; attempting to cache/memoize state in views during queries.","solutions":["Move the mutating logic into a contract operation; services may only read views","In the service, replace save()/write calls with read-only getters (get, load, etc.)","If a query should change state, have the client submit the corresponding operation via GraphQL instead","Keep shared state-manipulation code in modules callable only from the contract side"],"exampleFix":"// before (service handle_query)\nasync fn handle_query(&self, _: Query) -> Result<...> {\n    self.counter.set(self.counter.get().await? + 1);\n    self.counter.save().await?; // panics: write_batch on service\n}\n\n// after (read-only service)\nasync fn handle_query(&self, _: Query) -> Result<...> {\n    Ok(self.counter.get().await?)\n}\n// increments happen in the contract's operation entrypoint","handlingStrategy":"validation","validationCode":"// In the service, only touch read APIs; guard against accidental persistence:\n#[cfg(debug_assertions)]\nfn assert_read_only<T: std::fmt::Debug>(_: &T) {} // placeholder for lint hooks\n// Review rule: handle_query must never call `.save().await` or mutate collections.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep state-mutating code in modules imported only by the contract, not the service","Code-review every handle_query for save/write calls","Remember the protocol: queries are free and read-only; operations (in blocks) mutate state"],"tags":["sdk","views","service","read-only","smart-contract","state-mutation"],"backgroundTag":"read-only-violation","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}