nautechsystems/nautilus_trader · critical

invalid `AggressorSide` enum string value, was '{value}'

Error message

invalid `AggressorSide` enum string value, was '{value}'

What it means

This panic comes from `aggressor_side_from_cstr`, a C FFI wrapper that converts a C string into the Rust `AggressorSide` enum. It fires when the input string does not match any `AggressorSide` variant when parsed via `FromStr`. Because a Rust panic crossing the FFI boundary would be undefined behavior, the wrapper runs inside `abort_on_panic`, so invalid input aborts the process rather than returning an error. Only exact canonical variant strings (as produced by `aggressor_side_to_cstr`) are accepted.

Source

Thrown at crates/model/src/ffi/enums.rs:342

pub extern "C" fn aggressor_side_to_cstr(value: AggressorSide) -> *const c_char {
    str_to_cstr(value.as_ref())
}

/// Returns an enum from a C string.
///
/// # Safety
///
/// Assumes `ptr` is a valid C string pointer.
///
/// # Panics
///
/// Panics if the C string does not correspond to a valid `AggressorSide` variant.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn aggressor_side_from_cstr(ptr: *const c_char) -> AggressorSide {
    abort_on_panic(|| {
        let value = unsafe { cstr_as_str(ptr) };
        AggressorSide::from_str(value)
            .unwrap_or_else(|_| panic!("invalid `AggressorSide` enum string value, was '{value}'"))
    })
}

#[unsafe(no_mangle)]
pub extern "C" fn asset_class_to_cstr(value: AssetClass) -> *const c_char {
    str_to_cstr(value.as_ref())
}

/// Returns an enum from a C string.
///
/// # Safety
///
/// Assumes `ptr` is a valid C string pointer.
///
/// # Panics
///
/// Panics if the C string does not correspond to a valid `AssetClass` variant.
#[unsafe(no_mangle)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the exact failing string and compare it character-by-character with the canonical `AggressorSide` variant strings (get a reference via `aggressor_side_to_cstr` on a valid enum value).
  2. Derive strings from the library's `AggressorSide` enum via `aggressor_side_to_cstr` instead of hard-coding literals.
  3. Trim whitespace and fix casing so the string matches the enum's FromStr spelling exactly.
  4. If a version upgrade introduced this, diff your hard-coded strings against the current enum definition in the model crate.
  5. Validate the string against the accepted variant set before the FFI call, since failure aborts the process.

Example fix

// before (Python caller passing a guessed value)
side = "buy"
value = aggressor_side_from_cstr(side.encode())
// after
side = "BUY"  # canonical spelling; or derive via aggressor_side_to_cstr(AggressorSide.BUY)
value = aggressor_side_from_cstr(side.encode())
Defensive patterns

Strategy: validation

Validate before calling

VALID_AGGRESSOR_SIDES = {"NO_AGGRESSOR_SIDE", "BUY", "SELL"}  # verify against enum definition
assert side_str in VALID_AGGRESSOR_SIDES, f"bad AggressorSide: {side_str!r}"
value = aggressor_side_from_cstr(side_str.encode())

Type guard

def is_valid_aggressor_side(s: str) -> bool:
    return s in {"NO_AGGRESSOR_SIDE", "BUY", "SELL"}  # keep in sync with the enum

Prevention

When it happens

Trigger: Calling `aggressor_side_from_cstr(ptr)` from C/Python/Cython with a string that is not a valid `AggressorSide` serialization — e.g. lowercase 'buy'/'sell' instead of the canonical variant spelling, an empty string, whitespace-padded input, or a value whose spelling changed between library versions.

Common situations: Hand-writing enum strings in strategy configs instead of round-tripping through the library's to_cstr serialization; passing a Python str without case normalization; upgrading NautilusTrader where an enum variant was renamed; typos in adapter code.

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/d799616d08fe12a3. Report an issue: GitHub.