{"record":{"id":"95270c587af69354","repo":"wasmerio/wasmer","slug":"metering-transform-module-info-attempting-to-use","errorCode":null,"errorMessage":"Metering::transform_module_info: Attempting to use a `Metering` middleware from multiple modules.","messagePattern":"Metering::transform_module_info: Attempting to use a `Metering` middleware from multiple modules\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"lib/middlewares/src/metering.rs","lineNumber":169,"sourceCode":"impl<F: Fn(&Operator) -> u64 + Send + Sync + 'static> ModuleMiddleware for Metering<F> {\n    /// Generates a `FunctionMiddleware` for a given function.\n    fn generate_function_middleware<'a>(\n        &self,\n        _: LocalFunctionIndex,\n    ) -> Box<dyn FunctionMiddleware<'a> + 'a> {\n        Box::new(FunctionMetering {\n            cost_function: self.cost_function.clone(),\n            global_indexes: self.global_indexes.lock().unwrap().clone().unwrap(),\n            accumulated_cost: 0,\n        })\n    }\n\n    /// Transforms a `ModuleInfo` struct in-place. This is called before application on functions begins.\n    fn transform_module_info(&self, module_info: &mut ModuleInfo) -> Result<(), MiddlewareError> {\n        let mut global_indexes = self.global_indexes.lock().unwrap();\n\n        if global_indexes.is_some() {\n            panic!(\n                \"Metering::transform_module_info: Attempting to use a `Metering` middleware from multiple modules.\"\n            );\n        }\n\n        // Append a global for remaining points and initialize it.\n        let remaining_points_global_index = module_info\n            .globals\n            .push(GlobalType::new(Type::I64, Mutability::Var));\n\n        module_info\n            .global_initializers\n            .push(GlobalInit::I64Const(self.initial_limit as i64));\n\n        module_info.exports.insert(\n            \"wasmer_metering_remaining_points\".to_string(),\n            ExportIndex::Global(remaining_points_global_index),\n        );\n","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/wasmerio/wasmer/blob/8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5/lib/middlewares/src/metering.rs#L151-L187","documentation":"The Metering middleware stores per-module state (a global index for remaining points) inside itself. transform_module_info detects that this state was already set, meaning the same Metering instance is being applied to a second module, which would corrupt cost accounting. It panics to enforce one-Metering-per-module usage.","triggerScenarios":"Constructing one `Metering::new(cost_function, initial_points)` and adding it to the middleware chain of more than one module compilation (e.g. looping over several modules each with `.push_metering(metering.clone())` or reusing a shared Arc<Metering> across Module::new calls).","commonSituations":"Batch-compiling multiple Wasm modules with a single shared middleware; caching a middleware chain in app state and reusing it per request; misunderstanding that Metering holds mutable per-module state rather than being stateless.","solutions":["Create a fresh Metering instance for every module you compile (inside the per-module loop).","If you share middleware, wrap Metering construction in a closure/factory instead of sharing the instance.","Check whether the shared object is an Arc<Metering> and clone semantics silently reuse the same global state; replace with per-use construction."],"exampleFix":"// before\nlet metering = Arc::new(Metering::new(cost_fn, 10_000));\nfor wasm in modules {\n    Module::new_with_middleware(&engine, &wasm, vec![metering.clone()])?;\n}\n// after\nfor wasm in modules {\n    let metering = Arc::new(Metering::new(cost_fn, 10_000)); // fresh per module\n    Module::new_with_middleware(&engine, &wasm, vec![metering])?;\n}","handlingStrategy":"validation","validationCode":"// Never reuse a Metering across modules; enforce with a factory\nfn metering_chain() -> Vec<Arc<dyn Middleware>> {\n    vec![Arc::new(Metering::new(cost_fn, 10_000))] // fresh per call\n}","typeGuard":"fn assert_fresh_metering(m: &Metering) { /* construct per module; Metering carries per-module global state, so there is no safe shared-handle check — always build new */ }","tryCatchPattern":null,"preventionTips":["Construct a new Metering::new(...) inside every module-compilation loop iteration.","Never clone/Share an Arc<Metering> across Module::new calls.","Store a middleware factory (closure) rather than middleware instances in shared app state.","Write a test that compiles two modules with your middleware chain to catch accidental reuse."],"tags":["panic","middleware","metering","state-reuse"],"backgroundTag":"middleware-instance-reuse","analyzedSha":"8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5","analyzedAt":"2026-09-01T23:06:31.009Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}