aio-libs/aiohttp · error · ValueError

'{directory}' is not a directory

Error message

'{directory}' is not a directory

What it means

Raised as ValueError by StaticResource.__init__ when the resolved path exists but is not a directory (e.g. it points to a regular file). StaticResource can only serve from a directory, so passing a file path is rejected after the existence check passes.

Source

Thrown at aiohttp/web_urldispatcher.py:521

    def __init__(
        self,
        prefix: str,
        directory: PathLike,
        *,
        name: str | None = None,
        expect_handler: _ExpectHandler | None = None,
        chunk_size: int = DEFAULT_CHUNK_SIZE,
        show_index: bool = False,
        break_symlink_sandbox: bool = False,
        append_version: bool = False,
    ) -> None:
        super().__init__(prefix, name=name)
        try:
            directory = Path(directory).expanduser().resolve(strict=True)
        except FileNotFoundError as error:
            raise ValueError(f"'{directory}' does not exist") from error
        if not directory.is_dir():
            raise ValueError(f"'{directory}' is not a directory")
        self._directory = directory
        self._show_index = show_index
        self._chunk_size = chunk_size
        self._break_symlink_sandbox = break_symlink_sandbox
        self._expect_handler = expect_handler
        self._append_version = append_version

        self._routes = {
            "GET": ResourceRoute(
                "GET", self._handle, self, expect_handler=expect_handler
            ),
            "HEAD": ResourceRoute(
                "HEAD", self._handle, self, expect_handler=expect_handler
            ),
        }
        self._allowed_methods = set(self._routes)

    def url_for(  # type: ignore[override]

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Pass the containing directory: app.router.add_static('/static', Path(file).parent).
  2. To serve a single file, register a route that returns web.FileResponse(path) instead.
  3. Double-check the path with Path(p).is_dir() before add_static.

Example fix

# before
app.router.add_static('/static', '/srv/app/index.html')

# after
app.router.add_static('/static', '/srv/app')
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def ensure_is_dir(directory):
    p = Path(directory).expanduser().resolve()
    if not p.is_dir():
        raise ValueError(f"not a directory: {p}")
    return p

Type guard

from pathlib import Path

def is_directory_path(directory) -> bool:
    return Path(directory).expanduser().resolve().is_dir()

Try / catch

try:
    app.router.add_static('/static', directory)
except ValueError as e:
    if 'not a directory' in str(e):
        directory = Path(directory).parent  # fall back to parent dir
    app.router.add_static('/static', directory)

Prevention

When it happens

Trigger: Calling app.router.add_static('/static', '/path/to/index.html') — pointing at a file rather than its containing directory. Also pointing at a device node or socket.

Common situations: Confusing the directory path with a file path; copy-paste of a file path from config; intending to serve a single file but using add_static instead of a route returning FileResponse.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/f29dad22d642c240.json. Report an issue: GitHub.