dmtrKovalenko/fff · error

No watch callback registered. Call fff_set_watch_callback…

Error message

No watch callback registered. Call fff_set_watch_callback first.

What it means

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.

Solutions

  1. Call fff_set_watch_callback(handle, cb, user_data) before any fff_watch call on the instance.
  2. Register the callback at instance-creation time so it is always present.
  3. Check the order of your init code: create instance → set callback → start watch.

Example fix

// before
fff_watch(inst, NULL, &opts); // fails: no callback
// after
fff_set_watch_callback(inst, on_events, NULL);
fff_watch(inst, NULL, &opts);
Defensive patterns

Strategy: validation

Validate before calling

if (!watchCallbackRegistered) throw new Error('call fff_set_watch_callback before fff_watch');

Type guard

function canWatch(inst) { return inst && inst.watchCallbackRegistered === true; }

Try / catch

try { fffWatch(pattern, opts); } catch (e) { if (String(e).includes('No watch callback')) { fffSetWatchCallback(cb); fffWatch(pattern, opts); } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10). Data as JSON: /api/errors/8b5de07d5006818c. Report an issue: GitHub.

Appendix: source

Thrown at crates/fff-c/src/watch.rs:194

    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) };
        }
    });

    match result {
        Ok(id) => FffResult::ok_int(id.0 as i64),
        Err(e) => FffResult::err(&format!("Failed to subscribe: {}", e)),
    }
}

/// [`fff_watch`] adapter with flattened options, for FFI libraries that cannot
/// marshal pointer arrays inside structs (e.g. Node's `ffi-rs`).

View on GitHub (pinned to 7f8537e70f)