bevyengine/bevy · error
PassSpanGuard::end was never called for {}
Error message
PassSpanGuard::end was never called for {} What it means
`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}".
Source
Thrown at crates/bevy_render/src/diagnostic/mod.rs:241
///
/// Will panic on drop unless [`PassSpanGuard::end`] is called.
pub struct PassSpanGuard<'a, R: ?Sized, P> {
recorder: &'a R,
name: Cow<'static, str>,
marker: PhantomData<P>,
}
impl<R: RecordDiagnostics + ?Sized, P: Pass> PassSpanGuard<'_, R, P> {
/// End the span. You have to provide the same pass which was used to begin the span.
pub fn end(self, pass: &mut P) {
self.recorder.end_pass_span(pass);
core::mem::forget(self);
}
}
impl<R: ?Sized, P> Drop for PassSpanGuard<'_, R, P> {
fn drop(&mut self) {
panic!("PassSpanGuard::end was never called for {}", self.name)
}
}
impl<T: RecordDiagnostics> RecordDiagnostics for Option<Arc<T>> {
fn record_f32<N>(&self, command_encoder: &mut CommandEncoder, buffer: &BufferSlice, name: N)
where
N: Into<Cow<'static, str>>,
{
if let Some(recorder) = &self {
recorder.record_f32(command_encoder, buffer, name);
}
}
fn record_u32<N>(&self, command_encoder: &mut CommandEncoder, buffer: &BufferSlice, name: N)
where
N: Into<Cow<'static, str>>,
{
if let Some(recorder) = &self {View on GitHub (pinned to 396ca72708)
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
Example fix
// before
let span = recorder.begin_pass_span("my_pass", &mut pass);
do_work(&mut pass)?; // early return drops the span -> panic
span.end(&mut pass);
// after
let span = recorder.begin_pass_span("my_pass", &mut pass);
let result = do_work(&mut pass);
span.end(&mut pass);
result? Defensive patterns
Strategy: validation
Prevention
- 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
When it happens
Trigger: 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.
Common situations: Custom render nodes that add timestamp spans around passes and later add fallible `?` operators inside the spanned region.
Related errors
- Expected {after:?} to exist
- Expected {before:?} to exist
- Texture format is not supported by the pipeline
- Cannot call `ReflectComponent::reflect_mut` on component {na
- Cannot call `ReflectComponent::reflect_unchecked_mut` on com
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/1b04e7e4a7b7c961.
Report an issue: GitHub.