dmtrKovalenko/fff · error
Unsupported FffWatchOptions version
Error message
Unsupported FffWatchOptions version {} (library understands up to {}) What it means
watch_options_from_ffi validates the version field of an FffWatchOptions struct passed to fff_watch. Version 0 or a version greater than FFF_WATCH_OPTIONS_VERSION is rejected because the library cannot safely interpret the layout of an unknown options struct. The error is returned as an owned FffResult pointer to the caller.
Solutions
- Rebuild the Rust shared library (make build) so the .so matches the Lua/bindings version.
- Set opts.version to FFF_WATCH_OPTIONS_VERSION exactly as defined in the installed library's header.
- Ensure the FffWatchOptions struct is fully initialized, not zeroed with version left at 0.
- If the plugin updated its options struct, update the C FFI bindings/header to match.
- Avoid mixing fff-nvim versions: delete stale build artifacts before reinstalling.
Example fix
// before
let opts = FffWatchOptions { version: 0, ..Default::default() };
fff_watch(inst, &opts); // unsupported version 0
// after
let opts = FffWatchOptions { version: FFF_WATCH_OPTIONS_VERSION, ..Default::default() };
fff_watch(inst, &opts); Defensive patterns
Strategy: validation
Validate before calling
assert(opts.version == C.FFF_WATCH_OPTIONS_VERSION,
('watch options version %d unsupported, need %d'):format(opts.version or 0, C.FFF_WATCH_OPTIONS_VERSION)) Type guard
local function valid_watch_opts(o) return type(o) == 'table' and o.version == C.FFF_WATCH_OPTIONS_VERSION end
Try / catch
local res = fff.watch(inst, opts)
if type(res) == 'userdata' and is_err_result(res) then
error('rebuild native lib: ' .. ffi.string(res.message))
end Prevention
- Always rebuild the Rust shared library together with the Lua code.
- Initialize the options struct's version field explicitly; never pass zeroed structs.
- Regenerate C headers/bindings whenever the options struct changes.
- Clear stale build artifacts when upgrading the plugin.
When it happens
Trigger: Calling fff_watch with FffWatchOptions.version set to 0 (uninitialized struct), a version from a newer fff-nvim release than the installed shared library, or garbage memory where the version field lands on 0/out-of-range.
Common situations: Lua side rebuilt/updated but the compiled Rust .so is stale (or vice versa) after upgrading the plugin; forgetting to set version = FFF_WATCH_OPTIONS_VERSION; a bindings codegen mismatch where struct layout changed; passing a zeroed struct without filling in version.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Unsupported FffCreateOptions version
- Query is null or invalid UTF-8
- File path is null or invalid UTF-8
- Failed to canonicalize path
- ignore_count > 0 but ignore is NULL
AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10).
Data as JSON: /api/errors/36a18ffd93cfd630.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fff-c/src/watch.rs:83
std::mem::forget(boxed);
p
};
Box::into_raw(Box::new(FffWatchEventBatch {
events: events_ptr,
count,
}))
}
unsafe fn watch_options_from_ffi(
opts: *const FffWatchOptions,
) -> Result<WatchOptions, *mut FffResult> {
if opts.is_null() {
return Ok(WatchOptions::default());
}
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")),
}
}View on GitHub (pinned to 7f8537e70f)