nautechsystems/nautilus_trader · error
load_state channel closed: {e}
Error message
load_state channel closed: {e} What it means
`load_state` offloads Redis reads to a blocking worker thread and waits for the result over a `tokio::sync::mpsc` channel. This error means the receiver side (`blocking_recv`) returned `RecvError`, i.e. the channel closed because the sending task was dropped without sending a result — the worker thread panicked or exited early.
Source
Thrown at crates/infrastructure/src/redis/cache.rs:1293
get_runtime().spawn(async move {
let result = async {
let full_key = format!("{trader_key}{REDIS_DELIMITER}{key}");
let value: Option<Bytes> = con.get(&full_key).await?;
let Some(value) = value else {
return Ok(AHashMap::new());
};
DatabaseQueries::deserialize_payload(encoding, &value)
}
.await;
if let Err(e) = tx.send(result) {
log::error!("Failed to send state load result for '{key}': {e:?}");
}
});
blocking_recv(&rx).map_err(|e| anyhow::anyhow!("load_state channel closed: {e}"))?
}
fn update_state(&self, key: String, state: &AHashMap<String, Bytes>) -> anyhow::Result<()> {
let payload = DatabaseQueries::serialize_payload(self.encoding(), state)?;
self.database.insert(key, Some(vec![Bytes::from(payload)]))
}
fn replace_list(&self, key: String, payload: Bytes) -> anyhow::Result<()> {
self.send_command(DatabaseOperation::ReplaceList, key, Some(vec![payload]))
}
}
#[async_trait::async_trait]
impl CacheDatabaseFactory for RedisCacheConfig {
async fn create(
&self,
trader_id: TraderId,
instance_id: UUID4,View on GitHub (pinned to 18893faf8b)
Solutions
- Check application logs for 'Failed to send state load result' or a panic message from the worker thread just before this error — that reveals the root cause
- Retry the load; transient panics during shutdown usually clear on a fresh run
- Validate the Redis keys/state payloads for the given key deserialize with the configured encoding before loading
- If reproducible, wrap the worker body in catch_unwind or report send failures through the channel instead of only logging
Defensive patterns
Strategy: retry
Try / catch
// Rust
match cache.load_state(key) {
Ok(state) => state,
Err(e) if e.to_string().contains("channel closed") => {
log::error!("worker died loading state {key}: {e:#}; retrying once");
cache.load_state(key)?
}
Err(e) => return Err(e),
} Prevention
- Check logs for worker panics/send failures before this error — it is always a symptom, never the root cause
- Validate Redis payloads deserialize with the configured encoding before load
- Avoid triggering loads during runtime shutdown
- Ensure spawned worker tasks cannot panic silently (guard deserialization with fallible APIs)
When it happens
Trigger: The background task spawned inside `load_state` panics before `tx.send(result)` (e.g. deserialization panic, poisoned mutex, OOM), or the task is dropped/aborted, so `rx` yields `Err(RecvError)`.
Common situations: Redis data in an unexpected format causing a panic in the loader task; async runtime shutdown during process exit; bugs in a forked/patched version of the cache where the spawned task fails silently (the original send failure is only logged, not surfaced).
Related errors
- load channel closed: {e}
- load_index_order_position channel closed: {e}
- load_index_order_client channel closed: {e}
- Stream receiver already taken
- Failed to send SetClient command: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1b4aeaecc852a3f6.
Report an issue: GitHub.