tirth8205/code-review-graph · critical · RuntimeError

watch update failed

Error message

watch update failed

What it means

Raised by WatchBatchProcessor.raise_if_failed after a filesystem-watch batch update raised an exception. The watcher catches the original failure (stored in self.failure) and re-raises it wrapped as RuntimeError('watch update failed') so the graph never silently goes stale.

Source

Thrown at code_review_graph/incremental.py:2284

                )
                if not changed_files:
                    return
                result = incremental_update(
                    repo_root,
                    store,
                    changed_files=changed_files,
                    reconcile_stale=False,
                )
                _raise_watch_update_errors(result, "incremental update")
                if result["files_updated"] > 0 and on_files_updated is not None:
                    postprocess_result = on_files_updated(store)
                    _raise_watch_postprocess_warnings(postprocess_result)
            except BaseException as exc:
                self.failure = exc

        def raise_if_failed(self) -> None:
            if self.failure is not None:
                raise RuntimeError("watch update failed") from self.failure

    processor = WatchBatchProcessor()
    debouncer = EventDebouncer(_DEBOUNCE_SECONDS, processor.process)

    class GraphUpdateHandler(FileSystemEventHandler):
        def dispatch(self, event: FileSystemEvent) -> None:
            if event.event_type not in {"created", "modified", "deleted", "moved"}:
                return
            if event.is_directory and event.event_type == "modified":
                return
            debouncer.handle_event(event)

        def start(self) -> None:
            debouncer.start()

        def stop(self) -> None:
            debouncer.stop()
            debouncer.join()

View on GitHub (pinned to b58668751a)

Solutions

  1. Inspect the __cause__ of the RuntimeError — the original exception identifies the real failure (e.g. sqlite3.OperationalError: database is locked).
  2. If it is a lock contention issue, ensure only one watcher/daemon instance runs per repo and stop conflicting processes.
  3. Delete the stale .code-review-graph state directory and let the watcher rebuild the graph from scratch.
  4. If the error persists, run a full non-incremental reindex to regenerate consistent graph state before restarting the watch.

Example fix

// before
updater.watch(repo_root)
// after
try:
    updater.watch(repo_root)
except RuntimeError as exc:
    log.error("watch failed: %s", exc.__cause__)
    rebuild_graph_state(repo_root)  # e.g. remove .code-review-graph and reindex
    updater.watch(repo_root)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    updater.watch(repo_root)
except RuntimeError as exc:
    if "watch update failed" in str(exc):
        logger.error("watch failed: %s", exc.__cause__)
        # let the daemon restart the watcher; optionally rebuild graph state

Prevention

When it happens

Trigger: Calling watch() on an IncrementalUpdater (or via the daemon 'start' command) when a debounced batch of filesystem events triggers a graph update that throws — e.g. a corrupt index DB, unparsable file, or IO error during postprocessing.

Common situations: Long-running watch daemon where a transient disk/SQLite error occurs mid-update; concurrent processes locking the graph database; graph state corrupted by a crashed prior run.

Related errors


AI-assisted analysis of tirth8205/code-review-graph@b58668751a (2026-08-28). Data as JSON: /api/errors/37cf077d27912660. Report an issue: GitHub.