nautechsystems/nautilus_trader · error · ValueError

height must be positive, was

Error message

height must be positive, was {self.height}

What it means

During Feather-to-Parquet conversion, ParquetDataCatalog::convert_feather_batches_to_parquet computes the (start_ts, end_ts) range from the batch's ts_init column via Self::ts_init_range (catalog.rs:4130). That helper fails when the batch has no ts_init column, the column is not UInt64, contains nulls, or the batch is empty; this error wraps that inner failure with the offending feather_path for context.

Solutions

  1. Inspect the Feather file's schema and ensure it contains a non-null UInt64 ts_init column (e.g. check batch.schema().index_of("ts_init") and the column type) before converting.
  2. If ts_init is missing, derive it from ts_event: use the use_ts_event_for_ts_init option in the stream conversion (which copies ts_event into ts_init), or rewrite the file adding the column.
  3. If ts_init is null in some rows, fix the upstream write that produced nulls, or filter out those rows before conversion.
  4. If the file is empty, delete or skip it; ts_init_range explicitly rejects empty batches.
  5. Check the inner error message (appended after the wrapped context) to see which of the four underlying conditions actually fired.

Example fix

// before: converting a feather batch lacking proper ts_init
let (start_ts, end_ts) = Self::ts_init_range(&batch)?;

// after: guard the schema before conversion
let ts_init = batch.schema().column_with_name("ts_init")
    .filter(|(_, f)| f.data_type() == &DataType::UInt64)
    .ok_or_else(|| anyhow::anyhow!("feather file must contain a UInt64 ts_init column"))?
assert!(batch.num_rows() > 0, "skip empty feather files");
Defensive patterns

Strategy: validation

Validate before calling

let schema = batch.schema();
let ok = schema.column_with_name("ts_init")
    .filter(|(_, f)| f.data_type() == &DataType::UInt64)
    .map(|(i, _)| batch.column(i).null_count() == 0)
    .unwrap_or(false);
assert!(ok && batch.num_rows() > 0, "batch must have non-null UInt64 ts_init and >0 rows");

Type guard

fn has_valid_ts_init(batch: &RecordBatch) -> bool {
    batch.schema().column_with_name("ts_init")
        .filter(|(_, f)| f.data_type() == &DataType::UInt64)
        .map(|(i, _)| batch.column(i).null_count() == 0)
        .unwrap_or(false)
        && batch.num_rows() > 0
}

Try / catch

match convert_stream_to_data(...).await {
    Err(e) if e.to_string().contains("ts_init range") => {
        // inspect feather schema, repair or skip the file
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling convert_stream_to_data (which calls convert_feather_batches_to_parquet) on a Feather file whose RecordBatch lacks a ts_init column, has ts_init typed as something other than UInt64Array, contains null values in ts_init, or is an empty/zero-row batch.

Common situations: Legacy or hand-written Feather files exported before the ts_init column convention existed; custom data written without NautilusTrader's standard timestamp columns; files produced by external ETL scripts that wrote i64 timestamps instead of UInt64; truncated/empty feather files left by a crashed conversion run.

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


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

Appendix: source

Thrown at python/nautilus_trader/analysis/config.py:316

        Whether to display NautilusTrader logo in the tearsheet.

    """

    charts: list[TearsheetChart] = field(default_factory=_default_charts)
    theme: str = "plotly_white"
    layout: GridLayout | None = None
    title: str = "NautilusTrader Backtest Results"
    include_benchmark: bool = True
    benchmark_name: str = "Benchmark"
    height: int = 1500
    show_logo: bool = True

    def __post_init__(self) -> None:
        """
        Validate the configuration.
        """
        if self.height <= 0:
            raise ValueError(f"height must be positive, was {self.height}")

    @property
    def chart_names(self) -> list[str]:
        """
        The names of the configured charts, in order.
        """
        return [c.name for c in self.charts]

View on GitHub (pinned to 18893faf8b)