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
- Strip everything from the first NUL before converting: s.split('\0').next().unwrap() then pass that slice.
- Use CStr::from_bytes_until_n (or CString::new on trimmed bytes) instead of raw buffer-to-str conversion.
- Trim padding from fixed-size fields based on the field's documented length, not the full buffer.
- 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 converting fixed-size char buffers, cut at the first NUL (split('\0').next()).
- Never pass raw buffer-backed strings directly to C-string conversion without trimming padding.
- Prefer CStr::from_bytes_until_n for buffer-to-CString conversions.
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
- JSON string contains interior null bytes
- Invalid scientific notation exponent '{exponent}': must be a
- invalid `AggressorSide` enum string value, was '{value}'
- invalid `AssetClass` enum string value, was '{value}'
- invalid `InstrumentClass` enum string value, was '{value}'
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/897d93f321424af3.
Report an issue: GitHub.