nautechsystems/nautilus_trader · error

Error converting enum

Error message

Error converting enum

What it means

bar_specification_new is a C FFI constructor that receives the bar aggregation as a raw u8 discriminator and converts it with BarAggregation::from_repr(...).expect("Error converting enum"). If the u8 does not map to a valid BarAggregation variant, the expect panics. The library throws it because the FFI boundary cannot return a Result and treats an unknown discriminant as a caller contract violation.

Source

Thrown at crates/model/src/ffi/data/bar.rs:45

use crate::{
    data::bar::{Bar, BarSpecification, BarType},
    enums::{AggregationSource, BarAggregation, PriceType},
    identifiers::InstrumentId,
    types::{Price, Quantity},
};

/// # Panics
///
/// Panics if `aggregation` or `price_type` do not correspond to valid enum variants.
#[unsafe(no_mangle)]
pub extern "C" fn bar_specification_new(
    step: usize,
    aggregation: u8,
    price_type: u8,
) -> BarSpecification {
    let aggregation =
        BarAggregation::from_repr(aggregation as usize).expect("Error converting enum");
    let price_type = PriceType::from_repr(price_type as usize).expect("Error converting enum");
    BarSpecification::new(step, aggregation, price_type)
}

/// Returns a [`BarSpecification`] as a C string pointer.
#[unsafe(no_mangle)]
pub extern "C" fn bar_specification_to_cstr(bar_spec: &BarSpecification) -> *const c_char {
    str_to_cstr(&bar_spec.to_string())
}

#[unsafe(no_mangle)]
pub extern "C" fn bar_specification_hash(bar_spec: &BarSpecification) -> u64 {
    let mut h = DefaultHasher::new();
    bar_spec.hash(&mut h);
    h.finish()
}

#[unsafe(no_mangle)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass aggregation values only from the bindings' own BarAggregation enum (e.g. nautilus_model.core.data enums), not hand-picked integers.
  2. Rebuild/reinstall the package so the Cython bindings and Rust extension are from the same version.
  3. Validate the configured aggregation string (e.g. "MINUTE", "TICK") against the enum before lowering to a byte.
  4. If writing raw FFI, add a caller-side check that the byte is in the documented repr range before invoking.

Example fix

// before (Python FFI call)
bar_specification_new(1, 7, 1)  # 7 is not a valid BarAggregation
// after
bar_specification_new(1, int(BarAggregation.MINUTE), int(PriceType.LAST))
Defensive patterns

Strategy: validation

Validate before calling

# Python caller
VALID_AGGREGATIONS = set(range(1, len(BarAggregation) + 1))
assert aggregation in VALID_AGGREGATIONS, f"bad aggregation repr {aggregation}"

Type guard

def is_valid_aggregation(v: int) -> bool:
    try:
        BarAggregation(v)
        return True
    except ValueError:
        return False

Prevention

When it happens

Trigger: Calling the exported bar_specification_new from Python/FFI with an aggregation byte outside the valid BarAggregation repr range (e.g. 0, 255, or an int from a stale/foreign enum table).

Common situations: Version mismatch between the Cython-generated bindings and the compiled Rust extension (enum discriminants shifted), hand-rolled ctypes/cffi calls passing the wrong constant, or a script building specs from unvalidated config values.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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