nautechsystems/nautilus_trader · error

CString::new failed

Error message

CString::new failed

What it means

str_to_cstr in crates/core/src/ffi/string.rs:159 allocates a Rust String as a C string with CString::new(s).expect("CString::new failed") and leaks it as a raw pointer. CString::new fails only when the input contains an interior NUL ('\0') byte, since C strings cannot contain embedded nulls, so the expect panics in exactly that case.

Source

Thrown at crates/core/src/ffi/string.rs:159

/// Panics if `ptr` is not null but contains invalid UTF-8.
#[must_use]
pub unsafe fn optional_cstr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
    if ptr.is_null() {
        None
    } else {
        // SAFETY: Caller guarantees ptr is valid per function contract
        Some(unsafe { cstr_as_str(ptr) })
    }
}

/// Create a C string pointer to newly allocated memory from a [`&str`].
///
/// # Panics
///
/// Panics if the input string contains interior null bytes.
#[must_use]
pub fn str_to_cstr(s: &str) -> *const c_char {
    CString::new(s).expect("CString::new failed").into_raw()
}

/// Drops the C string memory at the pointer.
///
/// # Safety
///
/// Assumes `ptr` is a valid C string pointer.
///
/// # Panics
///
/// Panics if `ptr` is null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cstr_drop(ptr: *const c_char) {
    abort_on_panic(|| {
        assert!(!ptr.is_null(), "`ptr` was NULL");
        // SAFETY: Caller guarantees ptr was allocated by str_to_cstr
        let cstring = unsafe { CString::from_raw(ptr.cast_mut()) };
        drop(cstring);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Strip everything from the first NUL before converting: s.split('\0').next().unwrap() then pass that slice.
  2. Use CStr::from_bytes_until_n (or CString::new on trimmed bytes) instead of raw buffer-to-str conversion.
  3. Trim padding from fixed-size fields based on the field's documented length, not the full buffer.
  4. If you control both sides, pass lengths explicitly instead of relying on C-string semantics.

Example fix

// before
let ptr = str_to_cstr(std::str::from_utf8(&symbol_buf)?);  // symbol_buf = b"AAPL\0\0..."
// after
let s = std::str::from_utf8(&symbol_buf)?.split('\0').next().unwrap();
let ptr = str_to_cstr(s);
Defensive patterns

Strategy: validation

Validate before calling

let s = std::str::from_utf8(&buf)?;
assert!(!s.contains('\0'), "interior NUL would panic str_to_cstr");
let ptr = str_to_cstr(s);

Type guard

fn is_cstring_safe(s: &str) -> bool {
    !s.contains('\0')
}

Try / catch

// Avoid the panic entirely by using the Result-returning API
let c = CString::new(s).map_err(|_| "interior NUL byte in input")?;

Prevention

When it happens

Trigger: Calling str_to_cstr (directly or via unix_nanos_to_iso8601_cstr, bar_specification_to_cstr, bar_type_to_cstr) with a &str containing a '\0': a string sliced out of a fixed-size byte buffer that includes padding nulls, or user input containing literal NULs.

Common situations: Converting a fixed-size C char array (e.g. 20-byte symbol field) to &str without trimming at the first NUL; binary-ish payloads mistaken for text; timestamps or bar specs assembled from buffers with trailing zero padding.

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


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