dmtrKovalenko/fff · error
Pattern is not valid UTF-8
Error message
Pattern is not valid UTF-8
What it means
fff_watch accepts a NULL pattern to mean 'watch the entire indexed tree', but a non-NULL pattern must still be valid UTF-8. If cstr_to_str fails on the provided pattern, the call returns this error instead of starting the watch.
Solutions
- Ensure the pattern is a valid NUL-terminated UTF-8 string before calling fff_watch.
- If the pattern came from user input or a file, transcode it to UTF-8 first and reject/handle invalid sequences.
- Pass NULL (not an invalid pointer) to watch the whole indexed tree.
Example fix
// before
const char *pattern = getenv("GLOB"); // may be non-UTF-8 on some systems
fff_watch(inst, pattern, &opts);
// after
char *pattern = getenv("GLOB");
if (pattern && !is_valid_utf8(pattern)) pattern = NULL; // fall back to whole tree
fff_watch(inst, pattern, &opts); Defensive patterns
Strategy: validation
Validate before calling
function safePattern(p) {
if (p === null || p === undefined) return null;
if (Buffer.from(p, 'utf8').toString('utf8') !== Buffer.from(p, 'utf8').toString('utf8') || !isUtf8(Buffer.from(p))) throw new Error('pattern must be valid UTF-8');
return p;
} Type guard
function isUtf8Pattern(p) { return p === null || (typeof p === 'string' && isUtf8(Buffer.from(p))); } Try / catch
try { fffWatch(pattern, opts); } catch (e) { if (String(e).includes('valid UTF-8')) pattern = null; /* retry watching whole tree */ } Prevention
- Always produce patterns from UTF-8-safe sources (JS strings, UTF-8 files).
- NUL-terminate C strings when calling from C.
- Use NULL, not an empty/invalid pointer, to mean 'watch everything'.
When it happens
Trigger: Calling fff_watch / fff_watch_args with a pattern char* that points to non-UTF-8 bytes (invalid encoding, dangling pointer, or unterminated garbage).
Common situations: Patterns built from system APIs returning platform-native encodings (e.g. Windows ANSI paths) rather than UTF-8; passing a non-NUL-terminated buffer; reading the pattern from a file with a non-UTF-8 encoding.
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
- ignore entry is NULL or invalid 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/35257edee3626681.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fff-c/src/watch.rs:186
/// * `pattern` must be NULL or valid null-terminated UTF-8.
/// * `opts` must be NULL or a valid `FffWatchOptions` pointer.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fff_watch(
fff_handle: *mut c_void,
pattern: *const c_char,
opts: *const FffWatchOptions,
) -> *mut FffResult {
let inst = match unsafe { instance_ref(fff_handle) } {
Ok(i) => i,
Err(e) => return e,
};
// NULL pattern = watch the entire indexed tree ("" in core).
let pattern_str = if pattern.is_null() {
""
} else {
match unsafe { crate::cstr_to_str(pattern) } {
Some(s) => s,
None => return FffResult::err("Pattern is not valid UTF-8"),
}
};
let options = match unsafe { watch_options_from_ffi(opts) } {
Ok(o) => o,
Err(e) => return e,
};
if inst.watch_callback.get().is_none() {
return FffResult::err("No watch callback registered. Call fff_set_watch_callback first.");
}
let slot = Arc::clone(&inst.watch_callback);
let result = inst.picker.watch(pattern_str, options, move |id, events| {
if let Some((cb, user_data)) = slot.get() {
let batch = batch_into_raw(events);
unsafe { cb(id.0, batch, user_data) };
}
});
View on GitHub (pinned to 7f8537e70f)