tracel-ai/burn · error
AsyncPolicy should be able to send policy state.
Error message
AsyncPolicy should be able to send policy state.
What it means
AsyncPolicy::update sends a PolicyUpdate message to the autobatcher thread and panics if the send fails. A failed send means the receiving side is closed — the inference thread has already exited, typically because it panicked on an earlier message or saw RecvError after all AsyncPolicy handles were dropped. The policy update is silently lost as a panic instead of being applied.
Source
Thrown at crates/burn-rl/src/policy/async_policy.rs:300
let (action_sender, action_receiver) = std::sync::mpsc::channel();
let item = ActionItem {
sender: action_sender,
inference_state: states,
deterministic,
};
self.inference_state_sender
.send(InferenceMessage::ActionMessage(item))
.expect("should be able to send message to inference_server.");
let action = action_receiver
.recv()
.expect("AsyncPolicy should receive queued actions.");
(action.action, action.context)
}
fn update(&mut self, update: Self::PolicyState) {
self.inference_state_sender
.send(InferenceMessage::PolicyUpdate(update))
.expect("AsyncPolicy should be able to send policy state.")
}
fn state(&self) -> Self::PolicyState {
let (sender, receiver) = mpsc::channel();
self.inference_state_sender
.send(InferenceMessage::PolicyRequest(sender))
.expect("should be able to send message to inference_server.");
receiver
.recv()
.expect("AsyncPolicy should be able to receive policy state.")
}
fn to_device(self, device: &Device) -> Self {
self.inference_state_sender
.send(InferenceMessage::ToDevice(device.clone()))
.expect("AsyncPolicy should be able to send policy state.");
self
}View on GitHub (pinned to d16f7ba2ed)
Solutions
- Fix the original autobatcher-thread failure visible in logs before this call.
- Recreate the AsyncPolicy (and re-apply the missed update) with AsyncPolicy::new after confirming the thread is dead.
- Ensure inner_policy.update/PolicyState records match the running policy's device and architecture to avoid server-thread panics.
- For robustness, have the server thread log-and-continue on recoverable message errors rather than unwinding.
Example fix
// before
self.inference_state_sender
.send(InferenceMessage::PolicyUpdate(update))
.expect("AsyncPolicy should be able to send policy state.")
// after
if let Err(err) = self.inference_state_sender.send(InferenceMessage::PolicyUpdate(update)) {
log::error!("Failed to deliver policy update; inference server is down: {}", err);
*self = AsyncPolicy::new(self.autobatch_size, self.rebuild_inner_policy());
// re-apply the update on the new server
} Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the server is responsive before pushing a policy update
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut p = policy.clone();
let _ = p.forward(sample_observation.clone());
})).map_err(|_| anyhow::anyhow!("inference server dead; cannot apply update"))?; Try / catch
if let Err(_panicked) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| policy.update(new_state))) {
log::error!("policy update lost: inference server is down; rebuilding and re-applying");
policy = AsyncPolicy::new(autobatch_size, inner_policy.clone());
policy.update(new_state);
} Prevention
- Ensure PolicyState records match the running policy's device/architecture to avoid server-thread panics during update_policy.
- Snapshot/apply updates before dropping other AsyncPolicy handles.
- Fix any earlier autobatcher-thread failure visible in logs before sending updates.
- Consider buffering updates so a transient server death does not silently lose training progress.
When it happens
Trigger: Calling update() after the server thread died: an earlier panic in the autobatcher loop (e.g. inside update_policy's flush, or inner_policy.update panicking on incompatible records), or the training driver dropped all other AsyncPolicy clones causing thread exit while a learner still calls update.
Common situations: Learner/actor split setups where the actor processes die and the learner's update() then panics on the dead channel; optimizer step errors (record mismatch, wrong device) crashing the server thread before subsequent updates arrive.
Related errors
- Autobatcher should be able to send current policy state.
- Can send message to autobatcher.
- Should be able to send message to inference_server
- AsyncPolicy should receive queued probabilities.
- should be able to send message to inference_server.
AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05).
Data as JSON: /api/errors/2ccce126544a648c.
Report an issue: GitHub.