nautechsystems/nautilus_trader · error

Flush command should not be drained

Error message

Flush command should not be drained

What it means

Companion to the Close panic: in `drain_buffer` (crates/infrastructure/src/redis/cache.rs), encountering `DatabaseOperation::Flush(_)` in the drained buffer panics with "Flush command should not be drained". Flush is a control/lifecycle command that the command processor must intercept; finding it in the drainable operation buffer indicates the internal separation between control commands and data operations broke.

Source

Thrown at crates/infrastructure/src/redis/cache.rs:870

                    log::error!("Null `payload` for `replace_list`");
                }
            }
            DatabaseOperation::Delete => {
                log::debug!(
                    "Processing DELETE for collection: {}, key: {}, payload: {:?}",
                    collection,
                    key,
                    msg.payload.as_ref().map(std::vec::Vec::len)
                );
                // `payload` can be `None` for a delete operation
                if let Err(e) = delete(&mut pipe, collection, &key, msg.payload) {
                    log::error!("{e}");
                } else {
                    has_pending_ops = true;
                }
            }
            DatabaseOperation::Close => panic!("Close command should not be drained"),
            DatabaseOperation::Flush(_) => panic!("Flush command should not be drained"),
        }
    }

    flush_pending_pipeline(conn, &mut pipe, &mut has_pending_ops).await;
}

async fn flush_pending_pipeline(
    conn: &mut ConnectionManager,
    pipe: &mut Pipeline,
    has_pending_ops: &mut bool,
) {
    if !*has_pending_ops {
        return;
    }

    if let Err(e) = pipe.query_async::<()>(conn).await {
        log::error!("{e}");
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Route Flush through the dedicated control path (handle_command/process_commands match on Flush) and never push it into the drained buffer.
  2. Intercept Flush/Close variants before enqueueing database operations.
  3. Reproduce with RUST_BACKTRACE=1 to find the enqueue site and fix the routing.

Example fix

// before
buffer_tx.send(DatabaseOperation::Flush(pattern)).await?; // lands in drained buffer

// after
// invoke the cache's flush command API so handle_command consumes it directly
Defensive patterns

Strategy: validation

Validate before calling

debug_assert!(!matches!(op, DatabaseOperation::Flush(_)), "Flush must go through the control path");

Try / catch

// guard enqueue sites: if is_control_op(&op) { control_tx.send(op).await?; } else { buffer_tx.send(op).await?; }

Prevention

When it happens

Trigger: A `DatabaseOperation::Flush` being enqueued into the operation buffer and then drained by `drain_buffer` (via `process_commands`, `handle_command`, or `flush_buffer`) instead of being handled as an explicit cache flush command.

Common situations: Calling cache flush concurrently with buffered writes so the Flush op lands in the queue; custom integrations enqueueing Flush into the data channel; version mismatches where Flush routing changed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/99ea946c8faaf576. Report an issue: GitHub.