{"record":{"id":"c37e628f3d087a2d","repo":"clockworklabs/SpacetimeDB","slug":"reducer-ran-out-of-energy","errorCode":null,"errorMessage":"reducer ran out of energy","messagePattern":"reducer ran out of energy","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/core/src/host/host_controller.rs","lineNumber":278,"sourceCode":"impl From<ReducerCallResult> for Result<(), anyhow::Error> {\n    fn from(value: ReducerCallResult) -> Self {\n        value.outcome.into_result()\n    }\n}\n\n#[derive(Clone, Debug)]\npub enum ReducerOutcome {\n    Committed,\n    Failed(Box<Box<str>>),\n    BudgetExceeded,\n}\n\nimpl ReducerOutcome {\n    pub fn into_result(self) -> anyhow::Result<()> {\n        match self {\n            Self::Committed => Ok(()),\n            Self::Failed(e) => Err(anyhow::anyhow!(e)),\n            Self::BudgetExceeded => Err(anyhow::anyhow!(\"reducer ran out of energy\")),\n        }\n    }\n\n    pub fn is_err(&self) -> bool {\n        !matches!(self, Self::Committed)\n    }\n}\n\nimpl From<&EventStatus> for ReducerOutcome {\n    fn from(status: &EventStatus) -> Self {\n        match &status {\n            EventStatus::Committed(_) => ReducerOutcome::Committed,\n            EventStatus::FailedUser(e) | EventStatus::FailedInternal(e) => {\n                ReducerOutcome::Failed(Box::new((&**e).into()))\n            }\n            EventStatus::OutOfEnergy => ReducerOutcome::BudgetExceeded,\n        }\n    }","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/fdd647dfac506d0967cc4492e9fce380706c94cb/crates/core/src/host/host_controller.rs#L260-L296","documentation":"Every reducer call is metered with an energy budget; the host aborts the reducer and rolls back its transaction when the budget is spent before the reducer returns. The host models this as ReducerOutcome::BudgetExceeded, and ReducerOutcome::into_result converts that variant into this anyhow error. Seeing it means the reducer was killed for resource exhaustion, not for a logic failure.","triggerScenarios":"A reducer loops over a large table, a long init/migration reducer runs during publish, or a scheduled reducer executes with the default system budget and consumes more energy than the CallReducerParams budget allotted; into_result() then surfaces 'reducer ran out of energy'.","commonSituations":"Workload grows past the configured budget between deploys; a caller forgets or lowers the max energy setting; an unbounded loop in reducer code; a version upgrade changes energy accounting so previously-fitting reducers now exceed the budget.","solutions":["Raise the energy budget for the offending call (max_energy in the call/schedule parameters) and retry.","Optimize the reducer: cap iterations, use indexes, avoid full-table scans and nested loops.","Split the work: paginate across multiple reducer calls or scheduled increments.","Match on ReducerOutcome before calling into_result so budget exhaustion is handled distinctly from Failed(e)."],"exampleFix":"// before\nlet outcome = call_reducer(/* ... */);\noutcome.into_result()?; // opaque: reducer ran out of energy\n\n// after\nmatch outcome {\n    ReducerOutcome::BudgetExceeded => {\n        // shrink the batch or raise the budget, then reschedule\n    }\n    ReducerOutcome::Failed(e) => return Err(anyhow::anyhow!(e)),\n    ReducerOutcome::Committed => {}\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn is_budget_exceeded(o: &ReducerOutcome) -> bool {\n    matches!(o, ReducerOutcome::BudgetExceeded)\n}","tryCatchPattern":"Match on the ReducerOutcome/EventStatus before calling into_result(); only map Failed(e) through. Treat BudgetExceeded as a signal to raise the budget or shrink the batch and reschedule, not as a generic failure.","preventionTips":["Set explicit max_energy on calls that touch large tables instead of relying on defaults.","Cap loop iterations inside reducers and continue work via scheduled follow-up calls.","Alert on budget-exceeded outcomes so dataset growth is caught before hard failures."],"tags":["reducer","energy-budget","wasm","spacetimedb"],"backgroundTag":"execution-budget-exceeded","analyzedSha":"fdd647dfac506d0967cc4492e9fce380706c94cb","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}