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

  1. Ensure every element of the ignore array is a valid NUL-terminated UTF-8 C string.
  2. Exclude the NULL terminator slot from ignore_count — pass exactly the number of real entries.
  3. 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

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.

Related errors


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)