{"record":{"id":"36a18ffd93cfd630","repo":"dmtrKovalenko/fff","slug":"unsupported-fffwatchoptions-version-library-un","errorCode":null,"errorMessage":"Unsupported FffWatchOptions version {} (library understands up to {})","messagePattern":"Unsupported FffWatchOptions version (.+?) \\(library understands up to (.+?)\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fff-c/src/watch.rs","lineNumber":83,"sourceCode":"        std::mem::forget(boxed);\n        p\n    };\n\n    Box::into_raw(Box::new(FffWatchEventBatch {\n        events: events_ptr,\n        count,\n    }))\n}\n\nunsafe fn watch_options_from_ffi(\n    opts: *const FffWatchOptions,\n) -> Result<WatchOptions, *mut FffResult> {\n    if opts.is_null() {\n        return Ok(WatchOptions::default());\n    }\n    let opts = unsafe { &*opts };\n    if opts.version == 0 || opts.version > FFF_WATCH_OPTIONS_VERSION {\n        return Err(FffResult::err(&format!(\n            \"Unsupported FffWatchOptions version {} (library understands up to {})\",\n            opts.version, FFF_WATCH_OPTIONS_VERSION\n        )));\n    }\n\n    let mut ignore = Vec::with_capacity(opts.ignore_count as usize);\n    if opts.ignore_count > 0 {\n        if opts.ignore.is_null() {\n            return Err(FffResult::err(\"ignore_count > 0 but ignore is NULL\"));\n        }\n        for i in 0..opts.ignore_count as usize {\n            let entry = unsafe { *opts.ignore.add(i) };\n            match unsafe { crate::cstr_to_str(entry) } {\n                Some(s) if !s.is_empty() => ignore.push(s.to_string()),\n                Some(_) => {}\n                None => return Err(FffResult::err(\"ignore entry is NULL or invalid UTF-8\")),\n            }\n        }","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/dmtrKovalenko/fff/blob/7f8537e70f0ea1210f9acbbfc4640141105cdc78/crates/fff-c/src/watch.rs#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet opts = FffWatchOptions { version: 0, ..Default::default() };\nfff_watch(inst, &opts); // unsupported version 0\n// after\nlet opts = FffWatchOptions { version: FFF_WATCH_OPTIONS_VERSION, ..Default::default() };\nfff_watch(inst, &opts);","handlingStrategy":"validation","validationCode":"assert(opts.version == C.FFF_WATCH_OPTIONS_VERSION,\n  ('watch options version %d unsupported, need %d'):format(opts.version or 0, C.FFF_WATCH_OPTIONS_VERSION))","typeGuard":"local function valid_watch_opts(o)\n  return type(o) == 'table' and o.version == C.FFF_WATCH_OPTIONS_VERSION\nend","tryCatchPattern":"local res = fff.watch(inst, opts)\nif type(res) == 'userdata' and is_err_result(res) then\n  error('rebuild native lib: ' .. ffi.string(res.message))\nend","preventionTips":["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."],"tags":["ffi","versioning","abi-compatibility","validation"],"backgroundTag":"incompatible-source-type","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"}