{"record":{"id":"35257edee3626681","repo":"dmtrKovalenko/fff","slug":"pattern-is-not-valid-utf-8","errorCode":null,"errorMessage":"Pattern is not valid UTF-8","messagePattern":"Pattern is not valid UTF-8","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fff-c/src/watch.rs","lineNumber":186,"sourceCode":"/// * `pattern` must be NULL or valid null-terminated UTF-8.\n/// * `opts` must be NULL or a valid `FffWatchOptions` pointer.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn fff_watch(\n    fff_handle: *mut c_void,\n    pattern: *const c_char,\n    opts: *const FffWatchOptions,\n) -> *mut FffResult {\n    let inst = match unsafe { instance_ref(fff_handle) } {\n        Ok(i) => i,\n        Err(e) => return e,\n    };\n    // NULL pattern = watch the entire indexed tree (\"\" in core).\n    let pattern_str = if pattern.is_null() {\n        \"\"\n    } else {\n        match unsafe { crate::cstr_to_str(pattern) } {\n            Some(s) => s,\n            None => return FffResult::err(\"Pattern is not valid UTF-8\"),\n        }\n    };\n    let options = match unsafe { watch_options_from_ffi(opts) } {\n        Ok(o) => o,\n        Err(e) => return e,\n    };\n    if inst.watch_callback.get().is_none() {\n        return FffResult::err(\"No watch callback registered. Call fff_set_watch_callback first.\");\n    }\n\n    let slot = Arc::clone(&inst.watch_callback);\n    let result = inst.picker.watch(pattern_str, options, move |id, events| {\n        if let Some((cb, user_data)) = slot.get() {\n            let batch = batch_into_raw(events);\n            unsafe { cb(id.0, batch, user_data) };\n        }\n    });\n","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/dmtrKovalenko/fff/blob/7f8537e70f0ea1210f9acbbfc4640141105cdc78/crates/fff-c/src/watch.rs#L168-L204","documentation":"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.","triggerScenarios":"Calling fff_watch / fff_watch_args with a pattern char* that points to non-UTF-8 bytes (invalid encoding, dangling pointer, or unterminated garbage).","commonSituations":"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.","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."],"exampleFix":"// before\nconst char *pattern = getenv(\"GLOB\"); // may be non-UTF-8 on some systems\nfff_watch(inst, pattern, &opts);\n// after\nchar *pattern = getenv(\"GLOB\");\nif (pattern && !is_valid_utf8(pattern)) pattern = NULL; // fall back to whole tree\nfff_watch(inst, pattern, &opts);","handlingStrategy":"validation","validationCode":"function safePattern(p) {\n  if (p === null || p === undefined) return null;\n  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');\n  return p;\n}","typeGuard":"function isUtf8Pattern(p) { return p === null || (typeof p === 'string' && isUtf8(Buffer.from(p))); }","tryCatchPattern":"try { fffWatch(pattern, opts); } catch (e) { if (String(e).includes('valid UTF-8')) pattern = null; /* retry watching whole tree */ }","preventionTips":["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'."],"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"}