dmtrKovalenko/fff · error
watch callback must be a function
Error message
watch callback must be a function
What it means
watch() accepts either a bare callback or (options, callback); before doing anything else it verifies the resolved callback is a function. If you pass the arguments in the wrong order or omit the callback, it returns this error instead of crossing the FFI boundary.
Solutions
- Pass the callback as the last argument: finder.watch(options, cb) or finder.watch(cb)
- Verify the callback is a function before calling watch
- Check the watch() overload documentation for the exact argument order you are using
Example fix
// before
finder.watch({ pattern: '*.rs' }); // missing callback
// after
finder.watch({ pattern: '*.rs' }, (events) => { console.log(events); }); Defensive patterns
Strategy: validation
Validate before calling
if (typeof cb !== 'function') throw new TypeError('watch requires a callback function'); Type guard
function isFn(x) { return typeof x === 'function'; } Try / catch
const res = finder.watch(opts, cb); if (!res.ok) { /* res.error === 'watch callback must be a function' -> fix call site */ } Prevention
- Always pass the callback as the last argument to watch()
- Enable TypeScript or JSDoc checking so swapped arguments are caught at compile time
- Wrap watch setup in a helper that validates arguments once
When it happens
Trigger: Calling finder.watch() with only an options object, passing the callback as the first argument when a pattern is also given (argument order confusion), or passing a non-function (string/undefined) where the callback belongs.
Common situations: Refactoring from watch(cb) to watch({pattern}, cb) or vice versa and leaving arguments swapped; TypeScript-less call sites where the signature is unchecked.
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
- watch callback has been closed
- patterns array must have at least 1 element
- watch callback must be a function
- fff native library not found. Run `npx @ff-labs/fff-node…
- Unsupported platform
AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10).
Data as JSON: /api/errors/e7c854101e48cc84.
Report an issue: GitHub.
Appendix: source
Thrown at packages/fff-bun/src/finder.ts:678
options?: WatchOptions,
): Result<WatchUnsubscribe>;
watch(
patternOrCallback: string | WatchBatchCallback,
callbackOrOptions?: WatchBatchCallback | WatchOptions,
maybeOptions?: WatchOptions,
): Result<WatchUnsubscribe> {
// Overload shift: watch(cb, opts?) -> empty pattern = whole tree.
const noPattern = typeof patternOrCallback === "function";
const pattern = noPattern ? "" : patternOrCallback;
const callback = noPattern
? patternOrCallback
: (callbackOrOptions as WatchBatchCallback);
const options = noPattern
? (callbackOrOptions as WatchOptions | undefined)
: maybeOptions;
if (typeof callback !== "function") {
return err("watch callback must be a function");
}
const guard = this.ensureAlive();
if (!guard.ok) return guard;
const trampoline = this.ensureWatchTrampoline(guard.value);
if (!trampoline.ok) return trampoline;
const result = ffiWatch(guard.value, pattern, options?.ignore ?? []);
if (!result.ok) return result;
// No startup race: the threadsafe trampoline only runs on the JS event
// loop, so this synchronous set always precedes the first routing lookup.
const watchId = result.value;
this.watchHandlers.set(watchId, callback);
return { ok: true, value: () => this.unwatchById(watchId) };
}View on GitHub (pinned to 7f8537e70f)