gitbutlerapp/gitbutler · warning
Watcher callback failed
Error message
Watcher callback failed
What it means
watcherStart(projectId, cb) starts a Rust file watcher and delivers (err, event) over IPC. When err is set, the callback warns and returns without forwarding — the watcher keeps running but that event is dropped. The error originates on the Rust side (notify or inotify backend) or in IPC delivery, not in this TypeScript.
Source
Thrown at apps/lite/electron/src/watcher.ts:207
* This function is responsible for starting the watcher and ensuring that there's ever only one watcher per project.
*/
private async ensureProjectWatcher(projectId: string): Promise<ProjectWatcherState> {
// There are previous subscriptions to the project and a watcher is already running.
const existing = this.projectWatchers.get(projectId);
if (existing) return existing;
// There is already a watcher being started by a previous subscriber.
//
// This is needed for the case in which two subscribers want
// to subscribe to the same project at the same time (or very close to each other).
const pending = this.pendingProjectWatchers.get(projectId);
if (pending) return pending;
// Create a watcher.
const creation = watcherStart(projectId, (err, event) => {
if (err) {
// oxlint-disable-next-line no-console
console.warn("Watcher callback failed", err);
return;
}
this.forwardWatcherEvent(projectId, event);
})
.then((handle) => {
const watcherState: ProjectWatcherState = {
handle,
subscriptionIds: new Set(),
};
// Once the watcher has been started, store the handle in the state.
this.projectWatchers.set(projectId, watcherState);
return watcherState;
})
.finally(() => {
// Once the creation has been fulfilled, remove it from the pending map.
this.pendingProjectWatchers.delete(projectId);
});
View on GitHub (pinned to caf1f223d3)
Solutions
- On Linux, raise watch limits: sudo sysctl fs.inotify.max_user_watches=524288 (persist in /etc/sysctl.conf)
- If the project folder was moved or deleted, unsubscribe and re-subscribe with the corrected path
- Inspect the logged err payload to identify which notify backend failed
- After this warn, trigger a full project-state refetch so dropped events self-heal
Defensive patterns
Strategy: fallback
Validate before calling
function isWatchableDir(path: string): boolean {
return !path.startsWith("\\\\") && !/^(?:/proc|/sys|/dev)//.test(path);
} Type guard
interface WatcherError {
code: string;
message: string;
}
function isWatcherErrorPayload(x: unknown): x is WatcherError {
return typeof x === "object" && x !== null && "code" in x;
} Try / catch
watcherStart(projectId, (err, event) => {
if (isWatcherErrorPayload(err)) {
console.warn("Watcher callback failed", err);
scheduleFullRefetch(projectId); // recover from dropped events
return;
}
this.forwardWatcherEvent(projectId, event);
}); Prevention
- Tune inotify limits on Linux dev machines and CI
- Watch local paths only
- Debounce subscribe/unsubscribe storms
- Track warn frequency per project to catch pathological folders
When it happens
Trigger: An active project watcher hits the Linux inotify limit (fs.inotify.max_user_watches exhausted), the watched directory is deleted or renamed underneath the watcher, or an event flood or serialization problem makes the backend deliver an error object instead of an event (apps/lite/electron/src/watcher.ts:207).
Common situations: Large workspaces or many concurrent watchers on Linux; the project folder moved or removed while subscribed; exotic filesystems such as network mounts producing backend errors.
Related errors
- Failed to stop project watcher
- Failed to stop project watcher for ${projectId}
- Failed to stop project watchers during shutdown
- Errors occurred: {cmd_errors:?}
- Download of {url} failed with HTTP status: {response_code}
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/45af3aa5174b150b.
Report an issue: GitHub.