{"record":{"id":"d05774e669774127","repo":"dmtrKovalenko/fff","slug":"ignore-entry-is-null-or-invalid-utf-8","errorCode":null,"errorMessage":"ignore entry is NULL or invalid UTF-8","messagePattern":"ignore entry is NULL or invalid UTF-8","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fff-c/src/watch.rs","lineNumber":99,"sourceCode":"    let opts = unsafe { &*opts };\n    if opts.version == 0 || opts.version > FFF_WATCH_OPTIONS_VERSION {\n        return Err(FffResult::err(&format!(\n            \"Unsupported FffWatchOptions version {} (library understands up to {})\",\n            opts.version, FFF_WATCH_OPTIONS_VERSION\n        )));\n    }\n\n    let mut ignore = Vec::with_capacity(opts.ignore_count as usize);\n    if opts.ignore_count > 0 {\n        if opts.ignore.is_null() {\n            return Err(FffResult::err(\"ignore_count > 0 but ignore is NULL\"));\n        }\n        for i in 0..opts.ignore_count as usize {\n            let entry = unsafe { *opts.ignore.add(i) };\n            match unsafe { crate::cstr_to_str(entry) } {\n                Some(s) if !s.is_empty() => ignore.push(s.to_string()),\n                Some(_) => {}\n                None => return Err(FffResult::err(\"ignore entry is NULL or invalid UTF-8\")),\n            }\n        }\n    }\n\n    Ok(WatchOptions { ignore })\n}\n\n// The caller guarantees user_data is safe on the callback thread.\nstruct UserData(*mut c_void);\nunsafe impl Send for UserData {}\nunsafe impl Sync for UserData {}\n\n// Shared so a closure surviving an unwatch race never dangles.\n#[derive(Default)]\npub(crate) struct WatchCallbackSlot(Mutex<Option<(FffWatchCallback, UserData)>>);\n\nimpl WatchCallbackSlot {\n    fn get(&self) -> Option<(FffWatchCallback, *mut c_void)> {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/dmtrKovalenko/fff/blob/7f8537e70f0ea1210f9acbbfc4640141105cdc78/crates/fff-c/src/watch.rs#L81-L117","documentation":"While converting each entry of the ignore list from C string to Rust &str, cstr_to_str returned None: the entry pointer is NULL or the bytes are not valid UTF-8. The whole watch call is aborted so a partially-translated ignore list is never applied.","triggerScenarios":"Calling fff_watch with an ignore array containing a NULL element (count larger than actual array, trailing sentinel counted) or an element pointing to non-UTF-8 bytes (e.g. Latin-1 paths on some systems).","commonSituations":"Passing paths with non-UTF-8 encodings from C code; miscounting the array so a NULL terminator slot is included in ignore_count; memory corruption or premature free of the array.","solutions":["Ensure every element of the ignore array is a valid NUL-terminated UTF-8 C string.","Exclude the NULL terminator slot from ignore_count — pass exactly the number of real entries.","Sanitize/convert non-UTF-8 paths to UTF-8 (or skip them) before building the array."],"exampleFix":"// before\nchar *ignore[] = {\"target/\", \"*.log\", NULL};\nopts.ignore_count = 3; // includes NULL sentinel\n// after\nchar *ignore[] = {\"target/\", \"*.log\"};\nopts.ignore_count = 2; // real entries only, all valid UTF-8","handlingStrategy":"validation","validationCode":"ignoreEntries.forEach((s, i) => {\n  if (s == null) throw new Error(`ignore[${i}] is NULL`);\n  if (!isUtf8(Buffer.from(s))) throw new Error(`ignore[${i}] is not valid UTF-8`);\n});","typeGuard":"function isValidIgnoreEntry(e) { return typeof e === 'string' && e.length > 0 && Buffer.from(e, 'utf8').toString('utf8') === e; }","tryCatchPattern":"try { startWatch(opts); } catch (e) { if (String(e).includes('ignore entry is NULL')) console.error('ignore list contains a NULL or non-UTF-8 entry'); }","preventionTips":["Count exactly the real entries; never include a NULL terminator in ignore_count.","Transcode all paths to UTF-8 before building the array.","Filter out empty strings in the caller, matching the library's own filtering."],"tags":["ffi","utf-8","watch","encoding"],"backgroundTag":"invalid-argument-format","analyzedSha":"7f8537e70f0ea1210f9acbbfc4640141105cdc78","analyzedAt":"2026-09-10T07:26:53.407Z","contentChangedAt":"2026-09-10T07:26:53.407Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}