{"record":{"id":"1b04e7e4a7b7c961","repo":"bevyengine/bevy","slug":"passspanguard-end-was-never-called-for","errorCode":null,"errorMessage":"PassSpanGuard::end was never called for {}","messagePattern":"PassSpanGuard::end was never called for (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bevy_render/src/diagnostic/mod.rs","lineNumber":241,"sourceCode":"///\n/// Will panic on drop unless [`PassSpanGuard::end`] is called.\npub struct PassSpanGuard<'a, R: ?Sized, P> {\n    recorder: &'a R,\n    name: Cow<'static, str>,\n    marker: PhantomData<P>,\n}\n\nimpl<R: RecordDiagnostics + ?Sized, P: Pass> PassSpanGuard<'_, R, P> {\n    /// End the span. You have to provide the same pass which was used to begin the span.\n    pub fn end(self, pass: &mut P) {\n        self.recorder.end_pass_span(pass);\n        core::mem::forget(self);\n    }\n}\n\nimpl<R: ?Sized, P> Drop for PassSpanGuard<'_, R, P> {\n    fn drop(&mut self) {\n        panic!(\"PassSpanGuard::end was never called for {}\", self.name)\n    }\n}\n\nimpl<T: RecordDiagnostics> RecordDiagnostics for Option<Arc<T>> {\n    fn record_f32<N>(&self, command_encoder: &mut CommandEncoder, buffer: &BufferSlice, name: N)\n    where\n        N: Into<Cow<'static, str>>,\n    {\n        if let Some(recorder) = &self {\n            recorder.record_f32(command_encoder, buffer, name);\n        }\n    }\n\n    fn record_u32<N>(&self, command_encoder: &mut CommandEncoder, buffer: &BufferSlice, name: N)\n    where\n        N: Into<Cow<'static, str>>,\n    {\n        if let Some(recorder) = &self {","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/bevyengine/bevy/blob/396ca727080776bd313bb892423b7d94e03b81b4/crates/bevy_render/src/diagnostic/mod.rs#L223-L259","documentation":"`RecordDiagnostics::begin_pass_span` returns a `PassSpanGuard` that must be finished by calling `guard.end(&mut pass)` with the same pass; `end` records the span end and then `mem::forget`s the guard so `Drop` does not run. If the guard drops normally on any path where `end` was not called, its `Drop` impl panics with \"PassSpanGuard::end was never called for {name}\".","triggerScenarios":"Acquiring a pass span on a render/compute pass and letting it drop without `.end(pass)`: an early `?` or `return` between begin and end, storing the guard beyond its scope, or forgetting the call during refactor.","commonSituations":"Custom render nodes that add timestamp spans around passes and later add fallible `?` operators inside the spanned region.","solutions":["Call `guard.end(&mut pass)` on every path before the pass ends; bind the fallible body to a variable and `?` it after ending the span","Do not store `PassSpanGuard` in structs or collections; treat it as strictly scoped","Keep early-exit logic in an inner function so the span always wraps it symmetrically"],"exampleFix":"// before\nlet span = recorder.begin_pass_span(\"my_pass\", &mut pass);\ndo_work(&mut pass)?; // early return drops the span -> panic\nspan.end(&mut pass);\n\n// after\nlet span = recorder.begin_pass_span(\"my_pass\", &mut pass);\nlet result = do_work(&mut pass);\nspan.end(&mut pass);\nresult?","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat begin_pass_span/end as a scoped pair like lock/unlock","Move fallible work into a helper so ?-returns cannot skip guard.end()","Never store PassSpanGuard in structs or let it cross function boundaries"],"tags":["bevy","render","diagnostics","timestamps","drop-guard","panic"],"backgroundTag":"guard-drop-without-end","analyzedSha":"396ca727080776bd313bb892423b7d94e03b81b4","analyzedAt":"2026-08-20T16:12:39.808Z","contentChangedAt":"2026-08-20T16:12:39.808Z","schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}