Graphify-Labs/graphify · error · ImportError

watchdog not installed. Run: pip install watchdog

Error message

watchdog not installed. Run: pip install watchdog

What it means

graphify's live-watch feature (watch_for_changes / watch in graphify/watch.py) depends on the third-party 'watchdog' package to observe filesystem events. The function imports watchdog.observers lazily and converts an ImportError into a plain, actionable ImportError telling you to pip-install it. It fires only when the watchdog dependency is absent from the active interpreter, not when watching itself fails.

Solutions

  1. Install the dependency into the interpreter graphify runs under: pip install watchdog
  2. If graphify is a dependency of your project, add watchdog to your project's dependencies (or graphify's watch extra) so it is installed automatically
  3. Verify the right environment: `python -m pip show watchdog` using the same python that runs graphify (e.g. graphify-out/.graphify_python)
  4. If you do not need live watching, skip the watch API entirely — extraction and clustering do not require watchdog

Example fix

# before
graphify.watch.watch_for_changes(Path('.'), callback)  # ImportError: watchdog not installed

# after
# shell:
#   pip install watchdog
graphify.watch.watch_for_changes(Path('.'), callback)
Defensive patterns

Strategy: validation

Validate before calling

import importlib.util

if importlib.util.find_spec('watchdog') is None:
    raise SystemExit('watchdog missing — pip install watchdog before enabling live watch')

graphify.watch.watch_for_changes(Path('.'), callback=on_change, debounce=2.0)

Try / catch

try:
    graphify.watch.watch_for_changes(Path('.'), callback=on_change)
except ImportError as e:
    if 'watchdog' in str(e):
        print('live watch disabled: install watchdog to enable')
    else:
        raise

Prevention

When it happens

Trigger: Calling graphify.watch.watch_for_changes(...) (or any CLI path that starts a file watcher) in an environment where `import watchdog` raises ImportError. The try block imports watchdog.observers.Observer, PollingObserver, and watchdog.events.FileSystemEventHandler; any failure of that import chain triggers the re-raise.

Common situations: Installing graphify without its watch extras, using a different virtualenv/interpreter than the one where watchdog was installed, or a CI container that never installs optional filesystem-watching dependencies.

Related errors


AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14). Data as JSON: /api/errors/7774e4399ed1477c. Report an issue: GitHub.

Appendix: source

Thrown at graphify/watch.py:1838


def watch(watch_path: Path, debounce: float = 3.0) -> None:
    """
    Watch watch_path for new or modified files and auto-update the graph.

    For code-only changes: re-runs AST extraction + rebuild immediately (no LLM).
    For doc/paper/image changes: writes a needs_update flag and notifies the user
    to run /graphify --update (LLM extraction required).

    debounce: seconds to wait after the last change before triggering (avoids
    running on every keystroke when many files are saved at once).
    """
    try:
        from watchdog.observers import Observer
        from watchdog.observers.polling import PollingObserver
        from watchdog.events import FileSystemEventHandler
    except ImportError as e:
        raise ImportError("watchdog not installed. Run: pip install watchdog") from e

    last_trigger: float = 0.0
    pending: bool = False
    changed: set[Path] = set()

    # Load .graphifyignore patterns ONCE at startup so the handler does not
    # re-parse the file on every filesystem event. Watchdog's handler runs on
    # the observer thread and is invoked for every event the OS delivers
    # (Time Machine writes, Docker/Colima VM I/O, Spotlight indexing, …) —
    # without this short-circuit a busy volume can saturate a CPU core
    # discarding events one extension at a time. (gh-928)
    watch_root_for_ignore = watch_path.resolve()
    ignore_patterns = _load_graphifyignore(
        watch_root_for_ignore,
        gitignore=_read_build_gitignore(watch_path / _GRAPHIFY_OUT),
    )

    class Handler(FileSystemEventHandler):

View on GitHub (pinned to 7fe58b0b0f)