nautechsystems/nautilus_trader · error · ValueError
must not be None
Error message
{name} must not be None What it means
After copying ts_event into the ts_init slot, apply_stream_conversion_transforms rebuilds each RecordBatch with RecordBatch::try_new(schema, columns). arrow's try_new validates that every column matches the schema (field order, count, data types); any mismatch fails construction and is wrapped in this error. In this branch the only schema change is metadata (or the ts_init column swap), so a mismatch means the swapped column's type or the rebuilt schema is inconsistent.
Solutions
- Compare the data types of ts_event and ts_init in the source schema; cast ts_event to UInt64 before assigning it to the ts_init slot if they differ.
- Use arrow::compute::cast(columns[ts_event_idx].as_ref(), ts_init_type) when copying, then rebuild the batch.
- If types cannot be reconciled, run the conversion without use_ts_event_for_ts_init and fix the source data.
- Check that the writer that produced the data used a compatible arrow-rs version (type-level mismatch often comes from version drift).
Example fix
// before
columns[ts_init_idx] = columns[ts_event_idx].clone();
// after
columns[ts_init_idx] = arrow::compute::cast(
columns[ts_event_idx].as_ref(),
schema.field(ts_init_idx).data_type(),
)?; Defensive patterns
Strategy: type-guard
Validate before calling
let ts_event_t = batch.schema().field_with_name("ts_event")?.data_type();
let ts_init_t = batch.schema().field_with_name("ts_init")?.data_type();
assert_eq!(ts_event_t, ts_init_t, "ts_event/ts_init type mismatch prevents column swap"); Type guard
fn columns_swappable(batch: &RecordBatch) -> bool {
let s = batch.schema();
match (s.field_with_name("ts_event"), s.field_with_name("ts_init")) {
(Ok(a), Ok(b)) => a.data_type() == b.data_type(),
_ => false,
}
} Try / catch
match RecordBatch::try_new(schema.clone(), columns) {
Err(e) => return Err(anyhow::anyhow!("cast ts_event to ts_init type before swap: {e}")),
Ok(b) => b,
} Prevention
- Cast ts_event to ts_init's data type before copying the column
- Ensure writers emit both timestamp columns as UInt64
- Pin/align arrow-rs versions between writer and converter
When it happens
Trigger: The ts_event and ts_init columns have different Arrow data types (e.g. ts_event is Int64/Timestamp while ts_init is UInt64), so columns[ts_init_idx] = columns[ts_event_idx].clone() violates the schema, or a schema rebuild with modified metadata no longer matches the actual columns.
Common situations: Legacy data where ts_event was stored as Timestamp(nanoseconds) but ts_init as UInt64 — the column copy succeeds but batch construction fails; files written by a different arrow-rs version with subtly different metadata/type handling.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Chart name cannot be empty
- Failed to concatenate stream batches
- Failed to create new batch
- height must be positive, was
- pandas is required for report generation; install it with…
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/d80ca27c5271f06c.
Report an issue: GitHub.
Appendix: source
Thrown at python/nautilus_trader/analysis/tearsheet.py:107
if not TYPE_CHECKING:
go = None # type: ignore[assignment]
TRADING_DAYS_PER_YEAR = 252
_GRID_ONE_ROW_MAX_CHARTS = 2
_GRID_TWO_ROWS_MAX_CHARTS = 4
_GRID_THREE_ROWS_MAX_CHARTS = 6
_GRID_DEFAULT_ROWS = 4
_STATIC_IMAGE_SUFFIXES = frozenset({".png", ".jpg", ".jpeg", ".webp", ".svg", ".pdf"})
_CHART_REGISTRY: dict[str, Callable] = {}
def _require_not_none(value: object, name: str) -> None:
if value is None:
raise ValueError(f"{name} must not be None")
def _format_optional_iso8601(timestamp_ns: int | None) -> str:
if timestamp_ns is None:
return "N/A"
return unix_nanos_to_iso8601(timestamp_ns, nanos_precision=False)
def _format_optional_duration(start_ns: int | None, end_ns: int | None) -> str:
if start_ns is None or end_ns is None:
return "N/A"
return str(pd.Timedelta(end_ns - start_ns, unit="ns"))
def _to_returns_series(returns: pd.Series | Mapping | list | None) -> pd.Series:
pandas: Any = _require_pandas()
if returns is None:
return pandas.Series(dtype=float)View on GitHub (pinned to 18893faf8b)