{"record":{"id":"8b5de07d5006818c","repo":"dmtrKovalenko/fff","slug":"no-watch-callback-registered-call-fff-set-watch-c","errorCode":null,"errorMessage":"No watch callback registered. Call fff_set_watch_callback first.","messagePattern":"No watch callback registered\\. Call fff_set_watch_callback first\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fff-c/src/watch.rs","lineNumber":194,"sourceCode":"    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\n    match result {\n        Ok(id) => FffResult::ok_int(id.0 as i64),\n        Err(e) => FffResult::err(&format!(\"Failed to subscribe: {}\", e)),\n    }\n}\n\n/// [`fff_watch`] adapter with flattened options, for FFI libraries that cannot\n/// marshal pointer arrays inside structs (e.g. Node's `ffi-rs`).","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/dmtrKovalenko/fff/blob/7f8537e70f0ea1210f9acbbfc4640141105cdc78/crates/fff-c/src/watch.rs#L176-L212","documentation":"fff_watch delivers file-system events by invoking a callback registered earlier via fff_set_watch_callback. If no callback was registered on this instance, the watch cannot report events, so the call fails immediately instead of watching silently.","triggerScenarios":"Calling fff_watch or fff_watch_args on an instance without first calling fff_set_watch_callback for that same instance; calling on a freshly created instance; or after the callback slot was cleared/replaced.","commonSituations":"New developers wiring fff_watch without the registration step; high-level wrappers that register the callback only in some code paths; instance recreated but callback registration skipped.","solutions":["Call fff_set_watch_callback(handle, cb, user_data) before any fff_watch call on the instance.","Register the callback at instance-creation time so it is always present.","Check the order of your init code: create instance → set callback → start watch."],"exampleFix":"// before\nfff_watch(inst, NULL, &opts); // fails: no callback\n// after\nfff_set_watch_callback(inst, on_events, NULL);\nfff_watch(inst, NULL, &opts);","handlingStrategy":"validation","validationCode":"if (!watchCallbackRegistered) throw new Error('call fff_set_watch_callback before fff_watch');","typeGuard":"function canWatch(inst) { return inst && inst.watchCallbackRegistered === true; }","tryCatchPattern":"try { fffWatch(pattern, opts); } catch (e) { if (String(e).includes('No watch callback')) { fffSetWatchCallback(cb); fffWatch(pattern, opts); } }","preventionTips":["Register the watch callback immediately after instance creation.","Centralize instance setup so callback registration cannot be skipped.","Re-register the callback whenever the instance is recreated."],"tags":["ffi","watch","callback","initialization-order"],"backgroundTag":"missing-required-argument","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"}