tracel-ai/burn · error

Can send message to autobatcher.

Error message

Can send message to autobatcher.

What it means

AsyncPolicy::increment_agents sends an IncrementAgents message to the autobatcher thread via a std::sync::mpsc channel and panics if send fails. send only fails when the receiving side of the channel has been closed, which happens once the autobatcher thread has exited — either because all senders were dropped (it saw a RecvError and broke out of its loop) or because it panicked on an earlier message. After this panic the AsyncPolicy handle is permanently unusable.

Source

Thrown at crates/burn-rl/src/policy/async_policy.rs:241

                    },
                    Err(err) => {
                        log::error!("Error in AsyncPolicy : {}", err);
                        break;
                    }
                }
            }
        });

        Self {
            inference_state_sender: sender,
        }
    }

    /// Increment the number of agents using the inference server.
    pub fn increment_agents(&self, num: usize) {
        self.inference_state_sender
            .send(InferenceMessage::IncrementAgents(num))
            .expect("Can send message to autobatcher.")
    }

    /// Decrement the number of agents using the inference server.
    pub fn decrement_agents(&self, num: usize) {
        self.inference_state_sender
            .send(InferenceMessage::DecrementAgents(num))
            .expect("Can send message to autobatcher.")
    }
}

impl<P> Policy for AsyncPolicy<P>
where
    P: Policy + Send + 'static,
{
    type ActionContext = P::ActionContext;
    type PolicyState = P::PolicyState;

    type Observation = P::Observation;

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Check logs for the original autobatcher-thread panic or 'Error in AsyncPolicy' message; fix the root cause that killed the thread first.
  2. Recreate the AsyncPolicy (AsyncPolicy::new) once the server thread has died — a dead handle cannot be revived.
  3. Avoid panics in the inner policy (validate observation shapes/batching) so the server thread never unwinds.
  4. If a panic in the server thread is expected to be possible, wrap the thread body in catch_unwind or replace expects with error logging plus a restart mechanism.

Example fix

// before
let policy = policy.clone(); // stale clone from previous run; server thread already dead
policy.increment_agents(1);
// after
let policy = AsyncPolicy::new(autobatch_size, inner_policy.clone()); // rebuild after server death
policy.increment_agents(1);
Defensive patterns

Strategy: fallback

Validate before calling

// Before using a long-lived AsyncPolicy, verify the server is responsive
let probe = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    let mut p = policy.clone();
    p.increment_agents(0);
}));
if probe.is_err() { rebuild_policy(); }

Try / catch

let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| policy.increment_agents(1)));
if ok.is_err() {
    log::warn!("inference server dead; rebuilding AsyncPolicy");
    policy = AsyncPolicy::new(autobatch_size, inner_policy.clone());
}

Prevention

When it happens

Trigger: Calling increment_agents on an AsyncPolicy after the inference thread has terminated: an earlier expect panic in the autobatcher loop (e.g. a failed action reply send or a panic in inner_policy.action/forward), or every clone of the AsyncPolicy being dropped and recreated so the old thread exited with RecvError while a stale clone is still used.

Common situations: Multi-threaded RL environments where one worker's panic poisons the shared inference server and remaining workers then panic on their next increment_agents/action call; holding an AsyncPolicy clone across a restart of the training loop; a previous operation panicked inside the inner policy (e.g. shape mismatch) killing the server thread.

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/697486dd2f12f2ac. Report an issue: GitHub.