dmtrKovalenko/fff · error
ignore entry is NULL or invalid UTF-8
Error message
ignore entry is NULL or invalid UTF-8
What it means
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.
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.
Example fix
// before
char *ignore[] = {"target/", "*.log", NULL};
opts.ignore_count = 3; // includes NULL sentinel
// after
char *ignore[] = {"target/", "*.log"};
opts.ignore_count = 2; // real entries only, all valid UTF-8 Defensive patterns
Strategy: validation
Validate before calling
ignoreEntries.forEach((s, i) => {
if (s == null) throw new Error(`ignore[${i}] is NULL`);
if (!isUtf8(Buffer.from(s))) throw new Error(`ignore[${i}] is not valid UTF-8`);
}); Type guard
function isValidIgnoreEntry(e) { return typeof e === 'string' && e.length > 0 && Buffer.from(e, 'utf8').toString('utf8') === e; } Try / catch
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'); } Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Pattern is not valid UTF-8
- Query is null or invalid UTF-8
- File path is null or invalid UTF-8
- ignore_count > 0 but ignore is NULL
- No watch callback registered. Call fff_set_watch_callback…
AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10).
Data as JSON: /api/errors/d05774e669774127.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fff-c/src/watch.rs:99
let opts = unsafe { &*opts };
if opts.version == 0 || opts.version > FFF_WATCH_OPTIONS_VERSION {
return Err(FffResult::err(&format!(
"Unsupported FffWatchOptions version {} (library understands up to {})",
opts.version, FFF_WATCH_OPTIONS_VERSION
)));
}
let mut ignore = Vec::with_capacity(opts.ignore_count as usize);
if opts.ignore_count > 0 {
if opts.ignore.is_null() {
return Err(FffResult::err("ignore_count > 0 but ignore is NULL"));
}
for i in 0..opts.ignore_count as usize {
let entry = unsafe { *opts.ignore.add(i) };
match unsafe { crate::cstr_to_str(entry) } {
Some(s) if !s.is_empty() => ignore.push(s.to_string()),
Some(_) => {}
None => return Err(FffResult::err("ignore entry is NULL or invalid UTF-8")),
}
}
}
Ok(WatchOptions { ignore })
}
// The caller guarantees user_data is safe on the callback thread.
struct UserData(*mut c_void);
unsafe impl Send for UserData {}
unsafe impl Sync for UserData {}
// Shared so a closure surviving an unwatch race never dangles.
#[derive(Default)]
pub(crate) struct WatchCallbackSlot(Mutex<Option<(FffWatchCallback, UserData)>>);
impl WatchCallbackSlot {
fn get(&self) -> Option<(FffWatchCallback, *mut c_void)> {View on GitHub (pinned to 7f8537e70f)