Watchdog
raw JSON → 6.0.0 verified Tue May 12 auth: no python install: verified quickstart: stale
Watchdog is a Python library and shell utility that provides an API for monitoring file system events in real-time. It supports various operating systems by utilizing native APIs like inotify (Linux), FSEvents (macOS), and ReadDirectoryChangesW (Windows), falling back to polling when native APIs are unavailable. Currently at version 6.0.0, the library maintains an active development pace with major releases approximately annually.
pip install watchdog Common errors
error ModuleNotFoundError: No module named 'watchdog' ↓
cause The `watchdog` library has not been installed in your Python environment.
fix
pip install watchdog
error ImportError: cannot import name 'Observer' from 'watchdog' ↓
cause The `Observer` class is located within the `watchdog.observers` submodule, not directly under the top-level `watchdog` package.
fix
from watchdog.observers import Observer
error OSError: [Errno 28] No space left on device ↓
cause On Linux, this misleading error often indicates that the system's `inotify` watch limit (the maximum number of files/directories that can be monitored) has been reached.
fix
Increase the
fs.inotify.max_user_watches kernel parameter, e.g., by running echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf; sudo sysctl -p error RuntimeError: Observer is not running. ↓
cause This occurs when you attempt to call `stop()` or `join()` on an `Observer` instance that has either not been started yet or has already been stopped.
fix
Ensure
observer.start() is called before observer.stop() and observer.join(), and avoid calling stop() or join() multiple times on the same observer instance. Warnings
breaking Python 3.8 support was dropped in Watchdog v5.0.0. Python 3.7 support was dropped in v4.0.0. Ensure your Python environment is 3.9 or newer. ↓
fix Upgrade your Python interpreter to version 3.9 or later.
breaking In v5.0.0, several core classes and exceptions were renamed (e.g., `BaseObserverSubclassCallable` to `ObserverType`, `UnsupportedLibc` to `UnsupportedLibcError`). Keyword arguments are now enforced in core API calls. `InotifyConstants.IN_CLOSE` was also removed. ↓
fix Update class/exception names and ensure keyword arguments are used for API calls.
breaking In v6.0.0, the `inotify` backend now uses `select.poll()` instead of the deprecated `select.select()`, if available. Additionally, several unused functions from the `watchdog.utils.echo` module were removed. ↓
fix Update any custom `inotify` implementations to reflect `select.poll()` usage if directly interacting with low-level components. Remove any calls to the deprecated `echo` module functions.
gotcha Text editors like Vim often write to temporary files and then swap them in to replace the original, which may not trigger `on_modified` events. ↓
fix Configure your editor to disable features that involve backup files or temporary swaps, or consider monitoring the parent directory for `on_moved` events if file replacement is the expected pattern.
gotcha On BSD/macOS systems using `kqueue`, Watchdog opens file descriptors for monitored items. Hitting the operating system's maximum open file descriptor limit can prevent Watchdog from monitoring new files or directories. ↓
fix Increase the per-process file descriptor limit on your operating system (`ulimit -n` on Unix-like systems) or adjust your monitoring strategy to cover fewer paths if possible.
gotcha On Windows, the `ReadDirectoryChangesW` API has limitations: rename/movement events for directories may be reported before I/O is complete, and delete events for directories might be reported as file deletion events. ↓
fix Implement robust error handling and potentially introduce small delays when processing Windows-specific move/delete events to account for eventual consistency, and be prepared to infer directory deletions from file deletion events.
breaking Watchdog will raise a `FileNotFoundError` if the path being monitored does not exist when the observer is started (`observer.start()`). Ensure all paths passed to `observer.schedule()` are valid and accessible. ↓
fix Ensure that the directory or file path passed to `observer.schedule()` exists before starting the observer. Verify permissions if the path exists but is inaccessible, as this can also lead to similar errors.
gotcha Watchdog raises `FileNotFoundError: [Errno 2] No such file or directory` if the path specified for monitoring (either through `observer.schedule()` or directly to an `Inotify` backend) does not exist on the filesystem at the time monitoring starts. This is a common error indicating an invalid or non-existent path. ↓
fix Verify that the directory or file path you are attempting to monitor exists and is accessible before calling `observer.start()` or scheduling the watch. This error often suggests a typo in the path argument or that the target filesystem item was deleted unexpectedly.
Install
pip install 'watchdog[watchmedo]' Install compatibility verified last tested: 2026-05-12
python os / libc variant status wheel install import disk
3.10 alpine (musl) watchmedo sdist - 0.08s 20.5M
3.10 alpine (musl) watchdog sdist - 0.08s 18.4M
3.10 alpine (musl) watchmedo - - 0.08s 20.5M
3.10 alpine (musl) watchdog - - 0.08s 18.4M
3.10 slim (glibc) watchmedo wheel 1.7s 0.07s 22M
3.10 slim (glibc) watchdog wheel 1.6s 0.06s 19M
3.10 slim (glibc) watchmedo - - 0.06s 22M
3.10 slim (glibc) watchdog - - 0.06s 19M
3.11 alpine (musl) watchmedo sdist - 0.14s 22.7M
3.11 alpine (musl) watchdog sdist - 0.14s 20.3M
3.11 alpine (musl) watchmedo - - 0.17s 22.7M
3.11 alpine (musl) watchdog - - 0.16s 20.3M
3.11 slim (glibc) watchmedo wheel 1.8s 0.14s 24M
3.11 slim (glibc) watchdog wheel 1.6s 0.15s 21M
3.11 slim (glibc) watchmedo - - 0.14s 24M
3.11 slim (glibc) watchdog - - 0.14s 21M
3.12 alpine (musl) watchmedo sdist - 0.13s 14.5M
3.12 alpine (musl) watchdog sdist - 0.12s 12.1M
3.12 alpine (musl) watchmedo - - 0.13s 14.5M
3.12 alpine (musl) watchdog - - 0.13s 12.1M
3.12 slim (glibc) watchmedo wheel 1.6s 0.14s 16M
3.12 slim (glibc) watchdog wheel 1.5s 0.15s 13M
3.12 slim (glibc) watchmedo - - 0.15s 16M
3.12 slim (glibc) watchdog - - 0.14s 13M
3.13 alpine (musl) watchmedo sdist - 0.12s 14.3M
3.13 alpine (musl) watchdog sdist - 0.12s 11.9M
3.13 alpine (musl) watchmedo - - 0.12s 14.2M
3.13 alpine (musl) watchdog - - 0.12s 11.8M
3.13 slim (glibc) watchmedo wheel 1.6s 0.12s 15M
3.13 slim (glibc) watchdog wheel 1.5s 0.12s 12M
3.13 slim (glibc) watchmedo - - 0.13s 15M
3.13 slim (glibc) watchdog - - 0.14s 12M
3.9 alpine (musl) watchmedo sdist - 0.08s 20.0M
3.9 alpine (musl) watchdog sdist - 0.07s 17.9M
3.9 alpine (musl) watchmedo - - 0.08s 20.0M
3.9 alpine (musl) watchdog - - 0.10s 17.9M
3.9 slim (glibc) watchmedo wheel 2.0s 0.07s 21M
3.9 slim (glibc) watchdog wheel 1.9s 0.08s 18M
3.9 slim (glibc) watchmedo - - 0.08s 21M
3.9 slim (glibc) watchdog - - 0.07s 18M
Imports
- Observer
from watchdog.observers import Observer - FileSystemEventHandler
from watchdog.events import FileSystemEventHandler - LoggingEventHandler
from watchdog.events import LoggingEventHandler
Quickstart stale last tested: 2026-04-23
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class MyEventHandler(FileSystemEventHandler):
def on_any_event(self, event):
# Log any event for demonstration
print(f"Event type: {event.event_type} | Path: {event.src_path} | Is directory: {event.is_directory}")
if __name__ == "__main__":
path_to_watch = "."
event_handler = MyEventHandler()
observer = Observer()
observer.schedule(event_handler, path_to_watch, recursive=True)
observer.start()
try:
print(f"Monitoring directory: {path_to_watch}")
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()