bevyengine/bevy · error
The TimeSender channel should always be empty during render.
Error message
The TimeSender channel should always be empty during render. You might need to add the bevy::core::time_system to your app.
What it means
The render app's `send_time` system pushes `Instant::now()` to the main world through the capacity-1 `TimeSender` channel each frame; the main world's `time_system` drains it between frames. If the channel is still full when the render app runs, `try_send` fails with `TrySendError::Full` and the system panics: the receiving time pipeline never ran, typically because `TimePlugin`/bevy's `time_system` was removed or a custom runner skips it.
Source
Thrown at crates/bevy_render/src/lib.rs:527
fn renderer_is_ready(state: Res<RenderState>) -> bool {
matches!(*state, RenderState::Ready)
}
fn run_render_schedule(world: &mut World) {
world.resource_scope(|world, order: Mut<RenderScheduleOrder>| {
for &label in &order.labels {
let _ = world.try_run_schedule(label);
}
});
}
fn send_time(time_sender: Res<TimeSender>) {
// update the time and send it to the app world regardless of whether we render
if let Err(error) = time_sender.0.try_send(Instant::now()) {
match error {
bevy_time::TrySendError::Full(_) => {
panic!(
"The TimeSender channel should always be empty during render. \
You might need to add the bevy::core::time_system to your app."
);
}
bevy_time::TrySendError::Disconnected(_) => {
// ignore disconnected errors, the main world probably just got dropped during shutdown
}
}
}
}
/// Inserts a [`FutureRenderResources`] created from this [`RenderCreation`].
///
/// Returns true if creation was successful, false otherwise.
fn insert_future_resources(render_creation: &RenderCreation, main_world: &mut World) -> bool {
let primary_window = main_world
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
.single(main_world)View on GitHub (pinned to 396ca72708)
Solutions
- Keep `TimePlugin` (part of DefaultPlugins) in the main app
- In a custom runner, run the main world's `time_system` every frame before rendering
- Never run the render app twice per main-world update
Example fix
# before
App::new()
.add_plugins(DefaultPlugins.build().disable::<TimePlugin>())
.add_plugins(bevy::render::RenderPlugin::default());
# after
App::new().add_plugins(DefaultPlugins); // keep TimePlugin alive Defensive patterns
Strategy: validation
Prevention
- Keep TimePlugin enabled whenever the RenderApp runs
- In custom runners, run the main world's time_system every frame before rendering
- Never invoke the render schedule more often than the main world updates
When it happens
Trigger: Disabling or omitting `TimePlugin` (and its `time_system`) while still running the RenderApp; a custom runner that invokes the render app more often than it runs the main world's time system.
Common situations: Custom app runners (editor-like loops, headless render farms) that drive rendering manually; `DefaultPlugins.build().disable::<TimePlugin>()` setups that still add render plugins.
Related errors
- overflow when adding duration to instant
- overflow when subtracting duration from instant
- An elapsed time getter has not been provided to `Instant`. P
- DrawFunctions<{}> must be added to the world as a resource b
- Cannot call `ReflectComponent::reflect_mut` on component {na
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/eba2b8ea42fc0054.
Report an issue: GitHub.