{"record":{"id":"aa7e6d8a74ad8d4b","repo":"nautechsystems/nautilus_trader","slug":"failed-to-convert-c-string-to-utf-8","errorCode":null,"errorMessage":"Failed to convert C string to UTF-8","messagePattern":"Failed to convert C string to UTF-8","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/core/src/ffi/uuid.rs","lineNumber":51,"sourceCode":"    abort_on_panic(UUID4::new)\n}\n\n/// Returns a [`UUID4`] from C string pointer.\n///\n/// # Safety\n///\n/// Assumes `ptr` is a valid C string pointer.\n///\n/// # Panics\n///\n/// Panics if `ptr` cannot be cast to a valid C string.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn uuid4_from_cstr(ptr: *const c_char) -> UUID4 {\n    abort_on_panic(|| {\n        assert!(!ptr.is_null(), \"`ptr` was NULL\");\n        // SAFETY: Caller guarantees ptr is valid per function contract\n        let cstr = unsafe { CStr::from_ptr(ptr) };\n        let value = cstr.to_str().expect(\"Failed to convert C string to UTF-8\");\n        UUID4::from(value)\n    })\n}\n\n/// Return a borrowed *null-terminated* UTF-8 C string representing `uuid`.\n///\n/// The pointer remains valid for as long as the input `UUID4` reference lives - callers **must\n/// not** attempt to free it.\n#[unsafe(no_mangle)]\npub extern \"C\" fn uuid4_to_cstr(uuid: &UUID4) -> *const c_char {\n    abort_on_panic(|| uuid.to_cstr().as_ptr())\n}\n\n/// Compare two UUID values, returning `1` when they are equal and `0` otherwise.\n#[unsafe(no_mangle)]\npub extern \"C\" fn uuid4_eq(lhs: &UUID4, rhs: &UUID4) -> u8 {\n    abort_on_panic(|| u8::from(lhs == rhs))\n}","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/ffi/uuid.rs#L33-L69","documentation":"uuid4_from_cstr in crates/core/src/ffi/uuid.rs:51 builds a UUID4 from a C string pointer, converting the CStr to &str with to_str().expect(\"Failed to convert C string to UTF-8\") inside abort_on_panic (so the process aborts, not just unwinds). It panics when the pointed-to bytes are not valid UTF-8; note the abort_on_panic wrapper makes this failure harsher than the similar panics in ffi/string.rs.","triggerScenarios":"Calling uuid4_from_cstr with a pointer to non-UTF-8 bytes: a binary/misinterpreted UUID representation (raw 16 bytes instead of the hex string form), legacy-encoded text, or truncated multi-byte data.","commonSituations":"Passing a UUID as raw 16-byte binary instead of its canonical 36-char hyphenated hex string; an ID field produced by a non-UTF-8 system; buffer corruption between producer and FFI call.","solutions":["Serialize UUIDs as canonical lowercase hex strings (e.g. Python str(uuid.uuid4())) before crossing the boundary.","Never pass raw 16-byte UUID binaries to this API — convert to the hex string representation first.","Validate the string matches ^[0-9a-fA-F-]{36}$ on the producer side before the call.","Because this aborts the process, add producer-side unit tests covering every ID emission path to keep bad bytes from reaching the FFI boundary."],"exampleFix":"// before (Python producer)\nstruct.pack(\"16s\", uuid.uuid4().bytes)      # raw binary, not UTF-8 text\n// after\nstr(uuid.uuid4()).encode(\"utf-8\")           # '550e8400-e29b-41d4-...' hex string","handlingStrategy":"validation","validationCode":"# Python producer\nu = str(uuid.uuid4())\nimport re\nassert re.fullmatch(r\"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\", u)\nsend_to_ffi(u.encode(\"utf-8\"))","typeGuard":"def is_uuid4_text(value: bytes) -> bool:\n    import re, string\n    try:\n        s = value.decode(\"utf-8\")\n    except UnicodeDecodeError:\n        return False\n    return bool(re.fullmatch(r\"[0-9a-fA-F-]{36}\", s))","tryCatchPattern":null,"preventionTips":["Always serialize UUIDs as canonical hex strings, never raw 16-byte binaries.","Because uuid4_from_cstr aborts the process on panic, validate IDs on the producer side religiously.","Unit-test every ID emission path with canonical string UUIDs.","Never pass raw struct-packed UUID bytes to this FFI entry point."],"tags":["ffi","uuid","utf-8","abort"],"backgroundTag":"invalid-argument-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}