gitbutlerapp/gitbutler · warning
Failed to stop project watcher
Error message
Failed to stop project watcher
What it means
unsubscribe() drops a subscription; when the last subscriber for a project leaves, it calls projectWatcher.handle.stop() to tell the Rust watcher to shut down. A throwing stop() (an IPC failure) is caught and warned, and the watcher is removed from the map regardless — so the manager forgets it even if the OS-level watcher survives, leaking until process exit.
Source
Thrown at apps/lite/electron/src/watcher.ts:133
// Remove this subscription from all the sender (window) subscriptions.
// If this was the last subscription for this sender, we remove the sender from the map.
const senderIds = this.senderSubscriptions.get(subscription.senderId);
if (senderIds) {
senderIds.delete(subscriptionId);
if (senderIds.size === 0) this.senderSubscriptions.delete(subscription.senderId);
}
// Remove the project subscription.
// If this was the last subscription to the project, the watcher is stopped.
const projectWatcher = this.projectWatchers.get(subscription.projectId);
if (projectWatcher) {
projectWatcher.subscriptionIds.delete(subscriptionId);
if (projectWatcher.subscriptionIds.size === 0) {
try {
projectWatcher.handle.stop();
} catch (error) {
// oxlint-disable-next-line no-console
console.warn("Failed to stop project watcher", error);
}
this.projectWatchers.delete(subscription.projectId);
}
}
return true;
}
private removeSenderSubscriptions(senderId: number): void {
const subscriptionIds = this.senderSubscriptions.get(senderId);
if (!subscriptionIds) return;
for (const subscriptionId of subscriptionIds) this.removeSubscription(subscriptionId);
this.senderSubscriptions.delete(senderId);
}
/**View on GitHub (pinned to caf1f223d3)
Solutions
- Treat as benign when it occurs during shutdown; verify via backend logs that watchers actually stopped
- If reproducible mid-session, capture the logged error payload and check the Rust watcher implementation for the matching failure
- Restart the app to clear any leaked watchers
- File an issue with the warn payload if it correlates with window-close races
Defensive patterns
Strategy: try-catch
Validate before calling
// Only attempt the stop when this really is the last subscriber
if (projectWatcher.subscriptionIds.size === 0) {
/* safe to stop */
} Type guard
function isIpcGone(error: unknown): boolean {
return error instanceof Error && /destroyed|context bridge|closed/i.test(error.message);
} Try / catch
try {
projectWatcher.handle.stop();
} catch (error) {
console.warn("Failed to stop project watcher", error);
} finally {
this.projectWatchers.delete(subscription.projectId);
} Prevention
- Unsubscribe on window close before destroying the webContents
- Make the backend stop idempotent so double-stops are harmless
- Periodically reconcile manager state with live backend watchers
When it happens
Trigger: Unsubscribing the last subscriber for a project at apps/lite/electron/src/watcher.ts:133 while the stop() IPC fails: backend watcher thread already dead, renderer or IPC torn down mid-call, or the handle invalidated by an earlier error.
Common situations: Closing a Lite window that held the last subscription; rapid subscribe/unsubscribe churn; the Rust watcher having panicked earlier so later stops reject.
Related errors
- Failed to stop project watcher for ${projectId}
- Failed to stop project watchers during shutdown
- unable to log error to file
- Watcher callback failed
- tauri event emission doesn't fail in practice
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/b0361a63b23bc178.
Report an issue: GitHub.