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

  1. Ensure the pattern is a valid NUL-terminated UTF-8 string before calling fff_watch.
  2. If the pattern came from user input or a file, transcode it to UTF-8 first and reject/handle invalid sequences.
  3. 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

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


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)