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
- Inspect the __cause__ of the RuntimeError — the original exception identifies the real failure (e.g. sqlite3.OperationalError: database is locked).
- If it is a lock contention issue, ensure only one watcher/daemon instance runs per repo and stop conflicting processes.
- Delete the stale .code-review-graph state directory and let the watcher rebuild the graph from scratch.
- 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
- Run only one watcher per repository to avoid DB lock contention.
- Wrap watch() in supervisor restart logic — the error is designed to be restartable.
- Keep the .code-review-graph state on reliable storage; rebuild it after crashes.
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
- Not a directory: {resolved}
- No .git, .svn, or .code-review-graph directory in {resolved}
- Alias '{effective_alias}' is already in use by {existing.pat
- watch observer stopped: dead thread(s) {names}
- Path is not a directory: {resolved}
AI-assisted analysis of tirth8205/code-review-graph@b58668751a (2026-08-28).
Data as JSON: /api/errors/37cf077d27912660.
Report an issue: GitHub.