nautechsystems/nautilus_trader · error
close
Error message
close
What it means
EventStoreWriter::close flushes pending entries, writes the RunEnded entry, seals the run, and returns the final high watermark. This expect fires when close returns Err, e.g. the backend fails the final append, manifest update, or seal during shutdown. In this test suite it guards the batch-threshold test's expected final watermark of 7.
Source
Thrown at crates/event_store/src/writer/mod.rs:1094
);
let config = WriterConfig {
channel_capacity: 16,
max_batch_entries: 2,
max_batch_latency: Duration::from_secs(30),
halt_threshold: Duration::from_secs(30),
};
let clock = get_atomic_clock_static();
let boxed = Box::new(backend);
let writer = EventStoreWriter::spawn(boxed, clock, halt, config).expect("spawn");
for ts in 10_u64..16_u64 {
writer.submit(entry_draft(ts)).expect("submit");
}
let final_hwm = writer.close(run_ended_draft()).expect("close");
// 6 submits + 1 RunEnded == 7 entries, batch=2 -> 4 commits (3 size-driven + 1 close).
assert_eq!(final_hwm, 7);
assert_eq!(appends_seen.load(Ordering::SeqCst), 4);
}
#[rstest]
fn submit_signals_halt_when_stalled_past_threshold(
captured_halt: (HaltCallback, Arc<Mutex<Vec<HaltReason>>>),
) {
// Channel capacity 1 with a backend gate held closed forces a stall: the first
// submit fills the buffer, the writer thread blocks inside append_batch, and a
// subsequent submit can never enqueue before the halt threshold fires.
let (halt, captured) = captured_halt;
let inner = Arc::new(Mutex::new(MemoryBackend::new()));
inner.lock().open_run(manifest("run-halt")).expect("open");
let gate = Arc::new((Mutex::new(false), parking_lot::Condvar::new()));View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the EventStoreError returned inside close (e.g. Backend("disk failure")) and fix the backend failure before closing
- Ensure open_run was called on the backend before spawning the writer and submitting entries
- Check that the writer was not already halted (see halt callback captures) — halt latches make close fail after a stall or disk error
- Verify the run is not already sealed/ended; sealing twice is an invalid state transition
Example fix
// before
let final_hwm = writer.close(run_ended_draft()).expect("close");
// after
let final_hwm = writer
.close(run_ended_draft())
.unwrap_or_else(|e| panic!("writer close failed: {e:?}")); Defensive patterns
Strategy: try-catch
Validate before calling
let m = backend.manifest().map_err(|e| format!("backend unhealthy before close: {e}"))?;
assert_eq!(m.status, RunStatus::Open, "run must be open before close"); Type guard
fn writer_closeable(m: &RunManifest) -> bool {
m.status == RunStatus::Open
} Try / catch
match writer.close(run_ended_draft()) {
Ok(hwm) => println!("sealed at hwm {hwm}"),
Err(EventStoreError::Backend(msg)) => eprintln!("backend failure on close: {msg}"),
Err(e) => eprintln!("close failed: {e:?}"),
} Prevention
- Always open_run before spawning a writer and check every Result
- Monitor the halt callback; once a halt fires, close will fail
- Never seal or end a run twice
- Test shutdown paths against failing backends deliberately
When it happens
Trigger: Calling writer.close(run_ended_draft()) when the backend's append_batch fails (disk error, backend error like the FailingBackend's "disk failure"), when the run was never opened, or when the writer thread already halted due to backpressure/disk failure so the close entry cannot be committed.
Common situations: Tests exercising batch thresholds (max_batch_entries=2) with six submits plus RunEnded; shutdown paths where the underlying storage backend reports disk failure; closing a writer whose run is already sealed.
Related errors
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e0cd378376e5e480.
Report an issue: GitHub.