nautechsystems/nautilus_trader · error

submit second

Error message

submit second

What it means

Test panic from `.expect("submit second")` on the second `EventStoreWriter::submit`. Same `SubmitError` semantics as any submit: `Closed` if the writer/halt is terminal (possibly latched by the first submit stalling), `HaltSignaled` if this submit blocked past `halt_threshold`. Failure here means the writer degraded between the first and second entry.

Source

Thrown at crates/event_store/src/writer/mod.rs:1875

    #[rstest]
    fn record_snapshot_anchor_records_current_watermark_under_madsim() {
        let (wrapper, shared) = SharedMemory::new();
        shared
            .lock()
            .open_run(manifest("run-anchor"))
            .expect("open");

        let writer = EventStoreWriter::spawn(
            Box::new(wrapper),
            get_atomic_clock_static(),
            noop_halt(),
            WriterConfig::default(),
        )
        .expect("spawn");

        writer.submit(entry_draft(10)).expect("submit first");
        writer.submit(entry_draft(11)).expect("submit second");
        let anchor = writer
            .record_snapshot_anchor("cache://position-snapshots/P-1/0", "blake3:abc")
            .expect("record anchor");

        let backend = shared.lock();
        assert_eq!(anchor.high_watermark, 2);
        assert_eq!(
            backend.latest_snapshot_anchor().expect("latest anchor"),
            Some(anchor),
        );
    }
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Increase `WriterConfig::channel_capacity` so consecutive submits don't block on a full rendezvous channel.
  2. Raise `halt_threshold` above expected per-entry commit latency.
  3. Check whether the first submit or setup already fired a halt; once latched, submits always return `Closed`.
  4. Verify the writer thread is still alive (backend commits succeeding) between submits.

Example fix

// before
writer.submit(entry_draft(11)).expect("submit second");
// after
writer.submit(entry_draft(11))
    .unwrap_or_else(|e| panic!("submit second failed: {e:?}"));
Defensive patterns

Strategy: try-catch

Validate before calling

let in_flight = submitted_not_yet_acked;
assert!(in_flight < channel_capacity, "avoid blocking submits on a full channel");

Type guard

fn is_stall_err(e: &SubmitError) -> Option<(std::time::Duration, std::time::Duration)> {
    if let SubmitError::HaltSignaled { stalled_for, threshold } = e { Some((*stalled_for, *threshold)) } else { None }
}

Try / catch

match writer.submit(draft) {
    Err(SubmitError::HaltSignaled { stalled_for, threshold }) => {
        log::error!("backpressure: stalled {stalled_for:?} > {threshold:?}");
    }
    other => other.map(|_| ()),
}

Prevention

When it happens

Trigger: The first submit's stall fired the halt callback, latching `halted`, so the second submit returns `Closed`; channel capacity of 1 with a slow backend; writer thread exited after the first entry.

Common situations: Tiny `channel_capacity` plus simulated slow commits; halt threshold shorter than per-entry commit latency; a prior halt fired during setup making all submits fail.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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