nautechsystems/nautilus_trader · error · anyhow::Error
Failed to sort stream conversion batch: {e}
Error message
Failed to sort stream conversion batch: {e} What it means
This error wraps a failure from Arrow's `sort_to_indices`, used when the concatenated stream batch is not monotonic by its `ts_init` column and must be sorted ascending before writing to the catalog. The sort itself almost never fails unless the `ts_init` array is in an unexpected state (e.g. contains nulls, since `nulls_first: false` sorting on nulls, or the array downcast/type is wrong) or an internal Arrow error occurs. It is surfaced with the sort operation as the named culprit.
Source
Thrown at crates/persistence/src/backend/catalog.rs:4103
}
let mut batch = concat_batches(&schema, batches.iter())
.map_err(|e| anyhow::anyhow!("Failed to concatenate stream batches: {e}"))?;
if batch.num_rows() == 0 {
return Ok(None);
}
if !Self::is_record_batch_monotonic_by_ts_init(&batch)? {
let indices = sort_to_indices(
Self::ts_init_array(&batch)?,
Some(SortOptions {
descending: false,
nulls_first: false,
}),
None,
)
.map_err(|e| anyhow::anyhow!("Failed to sort stream conversion batch: {e}"))?;
batch = take_record_batch(&batch, &indices)
.map_err(|e| anyhow::anyhow!("Failed to reorder stream conversion batch: {e}"))?;
}
let ts_init = Self::ts_init_array(&batch)?;
if ts_init.null_count() > 0 {
anyhow::bail!("ts_init column contains null values");
}
Ok(Some(batch))
}
fn is_record_batch_monotonic_by_ts_init(batch: &RecordBatch) -> anyhow::Result<bool> {
let ts_init = Self::ts_init_array(batch)?;
if ts_init.null_count() > 0 {
anyhow::bail!("ts_init column contains null values");
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Read the inner Arrow error in {e} first: if it says `ts_init column not found` or `ts_init column is not UInt64`, fix the source data schema rather than the sort.
- Re-extract the stream data so messages carry valid, ordered `ts_event`/`ts_init` nanosecond timestamps.
- If the data legitimately has nulls in ts_init, repair the feather file (fill ts_init from ts_event) before converting.
- Convert with `use_ts_event_for_ts_init = true` if appropriate for the data type so ts_init is sourced from the always-populated ts_event column.
Example fix
// before: sorting a possibly null-bearing ts_init
let indices = sort_to_indices(Self::ts_init_array(&batch)?, Some(SortOptions { descending: false, nulls_first: false }), None)
.map_err(|e| anyhow::anyhow!("Failed to sort stream conversion batch: {e}"))?;
// after: validate ts_init before sorting
let ts_init = Self::ts_init_array(&batch)?;
if ts_init.null_count() > 0 {
anyhow::bail!("ts_init column contains null values; repair source data before sorting");
}
let indices = sort_to_indices(ts_init, Some(SortOptions { descending: false, nulls_first: false }), None)
.map_err(|e| anyhow::anyhow!("Failed to sort stream conversion batch: {e}"))?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: confirm ts_init is present, UInt64, and null-free before conversion
fn ts_init_is_clean(batch: &RecordBatch) -> anyhow::Result<()> {
let idx = batch.schema().index_of("ts_init")
.map_err(|_| anyhow::anyhow!("ts_init column not found"))?;
let arr = batch.column(idx).as_any()
.downcast_ref::<UInt64Array>()
.ok_or_else(|| anyhow::anyhow!("ts_init column is not UInt64"))?;
if arr.null_count() > 0 {
anyhow::bail!("ts_init column contains null values");
}
Ok(())
} Try / catch
// Rust
match catalog.convert_stream_file(path) {
Err(e) if e.to_string().contains("Failed to sort stream conversion batch") => {
// inspect the inner Arrow error for missing/typed ts_init
log::error!("sort failed for {path}: {e:#}");
}
other => other?,
} Prevention
- Ensure stream messages always carry monotonically sensible, populated ts_event/ts_init timestamps.
- Check for clock skew / NTP issues on machines recording live data.
- Pre-validate the ts_init column type (UInt64) and null count before conversion.
- Sort data upstream in extraction so the conversion path never needs the sort fallback.
When it happens
Trigger: In `apply_stream_conversion_transforms`, after `is_record_batch_monotonic_by_ts_init` returns false (out-of-order timestamps from unordered stream messages), the code sorts `ts_init_array(&batch)`. The error appears if `ts_init_array` fails (no `ts_init` column / not UInt64 — the message then embeds that Arrow error), or the sort kernel itself errors on a null-bearing or oversized array.
Common situations: Stream data written with timestamps that jump backwards (clock skew, out-of-order message replay), combined with a feather file whose `ts_init` column was written with a non-UInt64 type (e.g. Int64 from an older writer) or missing entirely from a custom data type.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- {type_name} timestamps must be in ascending order
- Failed to concatenate stream batches: {e}
- Failed to reorder stream conversion batch: {e}
- ts_init column is not UInt64
- Failed to merge custom data type metadata: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/930cc95fb3b85e82.
Report an issue: GitHub.