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

  1. Treat as benign when it occurs during shutdown; verify via backend logs that watchers actually stopped
  2. If reproducible mid-session, capture the logged error payload and check the Rust watcher implementation for the matching failure
  3. Restart the app to clear any leaked watchers
  4. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/b0361a63b23bc178. Report an issue: GitHub.