{"record":{"id":"0f64dbcad4a91808","repo":"langchain-ai/deepagents","slug":"notimplementederror-raised-by-abstract-ls-backe","errorCode":null,"errorMessage":"NotImplementedError raised by abstract `ls` (backend does not implement `ls`)","messagePattern":"NotImplementedError raised by abstract `ls` \\(backend does not implement `ls`\\)","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/backends/protocol.py","lineNumber":448,"sourceCode":"        \"created_at\": str,  # ISO format timestamp\n        \"modified_at\": str,  # ISO format timestamp\n    }\n    ```\n    \"\"\"\n\n    def ls(self, path: str) -> \"LsResult\":\n        \"\"\"List all files in a directory with metadata.\n\n        Args:\n            path: Absolute path to the directory to list. Must start with `'/'`.\n\n        Returns:\n            `LsResult` with directory entries or error.\n\n        Raises:\n            NotImplementedError: If the backend does not implement `ls`.\n        \"\"\"\n        raise NotImplementedError\n\n    async def als(self, path: str) -> \"LsResult\":\n        \"\"\"Async version of `ls`.\"\"\"\n        return await asyncio.to_thread(self.ls, path)\n\n    def read(\n        self,\n        file_path: str,\n        offset: int = 0,\n        limit: int = 2000,\n    ) -> ReadResult:\n        \"\"\"Read file content for the requested line range.\n\n        Implementations must tolerate degenerate windows rather than raising:\n        a negative `offset` reads from the first line, and a non-positive\n        `limit` returns empty content with every pagination field unset.\n        `deepagents.backends.utils.normalize_read_bounds` clamps both bounds for\n        implementations that slice in Python.","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/backends/protocol.py#L430-L466","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Implement `ls(self, path) -> LsResult` in your Backend subclass (or switch to a backend that does, e.g. the filesystem backend).","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.","Check which backend is actually wired into your agent configuration — the error means that instance lacks `ls`."],"exampleFix":"// before\nclass S3Backend(Backend):\n    def read(self, file_path, offset=0, limit=2000): ...\n// after\nclass S3Backend(Backend):\n    def read(self, file_path, offset=0, limit=2000): ...\n    def ls(self, path):\n        prefix = path.lstrip('/')\n        entries = list_bucket_dirs(prefix)\n        return LsResult(entries=entries)","handlingStrategy":"try-catch","validationCode":"if not hasattr(backend, 'ls') or type(backend).ls is Backend.ls:\n    raise RuntimeError('configured backend does not implement ls; skill discovery will fail')","typeGuard":"def supports_ls(backend) -> bool:\n    return getattr(type(backend).ls, '__override__', False) or type(backend).ls is not Backend.ls","tryCatchPattern":"try:\n    result = backend.ls(path)\nexcept NotImplementedError:\n    result = LsResult(entries=[], error='ls not supported by this backend')","preventionTips":["When writing a custom backend, implement the full abstract surface (ls, read, grep, glob, write, edit) or explicitly document gaps.","Run a startup capability check against your backend before wiring it into an agent that needs skill discovery.","Prefer extending an existing implemented backend rather than starting from the raw abstract class."],"tags":["not-implemented","abstract-method","backend-protocol"],"backgroundTag":"not-implemented","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}