langchain-ai/deepagents · error · NotImplementedError

NotImplementedError raised by abstract `ls` (backend does no

Error message

NotImplementedError raised by abstract `ls` (backend does not implement `ls`)

What it means

The base Backend class defines `ls` as an abstract method that raises NotImplementedError; subclasses must override it to enumerate directory entries. Calling `ls` on a backend that did not implement it — or on the bare protocol class — raises this error. It signals the chosen backend is read- or capability-limited (or partially implemented) and `ls` is unsupported.

Source

Thrown at libs/deepagents/deepagents/backends/protocol.py:448

        "created_at": str,  # ISO format timestamp
        "modified_at": str,  # ISO format timestamp
    }
    ```
    """

    def ls(self, path: str) -> "LsResult":
        """List all files in a directory with metadata.

        Args:
            path: Absolute path to the directory to list. Must start with `'/'`.

        Returns:
            `LsResult` with directory entries or error.

        Raises:
            NotImplementedError: If the backend does not implement `ls`.
        """
        raise NotImplementedError

    async def als(self, path: str) -> "LsResult":
        """Async version of `ls`."""
        return await asyncio.to_thread(self.ls, path)

    def read(
        self,
        file_path: str,
        offset: int = 0,
        limit: int = 2000,
    ) -> ReadResult:
        """Read file content for the requested line range.

        Implementations must tolerate degenerate windows rather than raising:
        a negative `offset` reads from the first line, and a non-positive
        `limit` returns empty content with every pagination field unset.
        `deepagents.backends.utils.normalize_read_bounds` clamps both bounds for
        implementations that slice in Python.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Implement `ls(self, path) -> LsResult` in your Backend subclass (or switch to a backend that does, e.g. the filesystem backend).
  2. If you only need file operations, avoid code paths that list directories (skill discovery, recursive delete checks) or provide an ls that returns an error LsResult instead of raising.
  3. Check which backend is actually wired into your agent configuration — the error means that instance lacks `ls`.

Example fix

// before
class S3Backend(Backend):
    def read(self, file_path, offset=0, limit=2000): ...
// after
class S3Backend(Backend):
    def read(self, file_path, offset=0, limit=2000): ...
    def ls(self, path):
        prefix = path.lstrip('/')
        entries = list_bucket_dirs(prefix)
        return LsResult(entries=entries)
Defensive patterns

Strategy: try-catch

Validate before calling

if not hasattr(backend, 'ls') or type(backend).ls is Backend.ls:
    raise RuntimeError('configured backend does not implement ls; skill discovery will fail')

Type guard

def supports_ls(backend) -> bool:
    return getattr(type(backend).ls, '__override__', False) or type(backend).ls is not Backend.ls

Try / catch

try:
    result = backend.ls(path)
except NotImplementedError:
    result = LsResult(entries=[], error='ls not supported by this backend')

Prevention

When it happens

Trigger: Calling backend.ls(path) on a minimal/partial Backend subclass; a backend that implements read/grep but not ls; code paths like discover_skill_dirs or _delete_target_may_have_descendants that internally call ls on a backend lacking it; instantiating Backend directly and calling ls.

Common situations: Plugging in a custom backend to restrict capabilities and forgetting `ls` is required for skill discovery; using a storage backend (e.g. object store) where directory listing is not implemented yet; tests against a stub backend.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/0f64dbcad4a91808. Report an issue: GitHub.