nautechsystems/nautilus_trader · error
load_custom_data channel closed: {e}
Error message
load_custom_data channel closed: {e} What it means
Raised by RedisCache::load_custom_data when blocking_recv on the result channel fails, i.e. the spawned worker that ran DatabaseQueries::load_custom_data terminated without sending a result (the oneshot/mpsc receiver reports the channel closed). It surfaces async Redis read failures to the synchronous caller.
Source
Thrown at crates/infrastructure/src/redis/cache.rs:485
/// test runtimes, plain threads).
///
/// # Errors
///
/// Returns an error if the query fails or the reply channel is closed.
pub fn load_custom_data(&self, data_type: &DataType) -> anyhow::Result<Vec<CustomData>> {
let con = self.con.clone();
let trader_key = self.trader_key.clone();
let data_type = data_type.clone();
let (tx, rx) = mpsc::channel();
get_runtime().spawn(async move {
let result = DatabaseQueries::load_custom_data(&con, &trader_key, &data_type).await;
if let Err(e) = tx.send(result) {
log::error!("Failed to send custom data result for '{data_type}': {e:?}");
}
});
blocking_recv(&rx).map_err(|e| anyhow::anyhow!("load_custom_data channel closed: {e}"))?
}
/// Sends an insert command for `key` with optional `payload` to Redis via the background task.
///
/// # Errors
///
/// Returns an error if the command cannot be sent to the background task channel.
pub fn insert(&self, key: String, payload: Option<Vec<Bytes>>) -> anyhow::Result<()> {
let op = DatabaseCommand::new(DatabaseOperation::Insert, key, payload);
match self.tx.send(op) {
Ok(()) => Ok(()),
Err(e) => anyhow::bail!("{FAILED_TX_CHANNEL}: {e}"),
}
}
/// Stores custom data in Redis (key format: `custom:<ts_init_020>:<uuid>`, value: full JSON).
///
/// # ErrorsView on GitHub (pinned to 18893faf8b)
Solutions
- Check the wrapped error and preceding logs (the worker logs 'Failed to send custom data result') for the root Redis error
- Verify the Redis server and connection are healthy, then retry the load
- Ensure the async runtime is not being shut down while load_custom_data is pending
- Confirm the trader key and data type exist; a persistent query failure can also abort the flow
Defensive patterns
Strategy: try-catch
Validate before calling
// check connection before loading custom data
if !cache.check_connection() {
anyhow::bail!("redis connection down; cannot load custom data");
} Try / catch
let data = match cache.load_custom_data(trader_key, data_type) {
Ok(d) => d,
Err(e) if e.to_string().contains("channel closed") => {
log::error!("load_custom_data worker died: {e:#}; retrying after reconnect");
cache.reconnect()?;
cache.load_custom_data(trader_key, data_type)?
}
Err(e) => return Err(e),
}; Prevention
- Keep the async runtime alive until in-flight loads complete
- Watch for the worker's 'Failed to send custom data result' log lines
- Validate trader key and data type before calling to avoid repeated query failures
When it happens
Trigger: Calling load_custom_data (or py_load_custom_data); the spawned query task is dropped, aborted, or panics before tx.send, so blocking_recv(&rx) errors and is wrapped as 'load_custom_data channel closed: {e}'.
Common situations: Redis connection error inside the async query, task cancellation during runtime shutdown, a panic in the query path, or runtime teardown racing with the load call.
Related errors
- Failed to send SetClient command: {e}
- Failed to send UpdateInstrument command: {e}
- {FAILED_TX_CHANNEL}: {e}
- Failed to flush database: {e}
- load_state channel closed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f1aef5cc77fff7f8.
Report an issue: GitHub.