BerriAI/litellm · error · NotImplementedError

adelete_sandbox must be implemented by provider

Error message

adelete_sandbox must be implemented by provider

What it means

BaseSandboxConfig.adelete_sandbox() is the abstract teardown hook for destroying a sandbox container; it must return bool. The base class raises NotImplementedError to force provider implementations. Encountering it means the delete path ran against the base or an incomplete custom config, so the container was NOT deleted (leaked resources).

Source

Thrown at litellm/llms/base_llm/sandbox/transformation.py:80

    async def arun_code(
        self,
        *,
        container: ContainerHandle | str,
        code: str,
        api_key: str | None = None,
        **kwargs,
    ) -> CodeExecutionResult:
        raise NotImplementedError("arun_code must be implemented by provider")

    async def adelete_sandbox(
        self,
        *,
        container: ContainerHandle | str,
        api_key: str | None = None,
        **kwargs,
    ) -> bool:
        raise NotImplementedError("adelete_sandbox must be implemented by provider")

    async def _read_capped_lines(self, response: httpx.Response) -> list[str]:
        lines: Final[list[str]] = []
        total = 0
        async for line in response.aiter_lines():
            total += len(line.encode("utf-8"))
            if total > SANDBOX_MAX_OUTPUT_BYTES:
                raise ValueError(
                    f"Sandbox output exceeded {SANDBOX_MAX_OUTPUT_BYTES} bytes; aborting to avoid unbounded memory use."
                )
            lines.append(line)
        return lines

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Implement `async def adelete_sandbox(self, *, container, api_key=None, **kwargs) -> bool` in your custom config.
  2. Switch to a fully implemented provider (OpenSandbox/E2B) if you did not intend to write a custom one.
  3. Until fixed, manually delete leaked containers via the provider's dashboard/API.

Example fix

# before
class MySandbox(BaseSandboxConfig):
    pass
await sandbox.adelete_sandbox(container=h)  # raises; container leaks

# after
class MySandbox(BaseSandboxConfig):
    async def adelete_sandbox(self, *, container, api_key=None, **kwargs) -> bool:
        resp = await self._client.delete(f"/containers/{container.id}")
        return resp.status_code in (200, 204)
Defensive patterns

Strategy: try-catch

Validate before calling

from litellm.llms.base_llm.sandbox.transformation import BaseSandboxConfig

assert type(cfg).adelete_sandbox is not BaseSandboxConfig.adelete_sandbox, "adelete_sandbox not implemented"

Type guard

from litellm.llms.base_llm.sandbox.transformation import BaseSandboxConfig

def supports_delete_sandbox(cfg: BaseSandboxConfig) -> bool:
    return type(cfg).adelete_sandbox is not BaseSandboxConfig.adelete_sandbox

Try / catch

try:
    ok = await cfg.adelete_sandbox(container=handle)
finally:
    if not ok:
        record_leaked_container(handle.id)  # ensure cleanup is trackable

Prevention

When it happens

Trigger: Calling the sandbox delete/cleanup API when the active provider config does not override adelete_sandbox — custom subclasses or a provider fallback to BaseSandboxConfig.

Common situations: Custom provider implementers who implemented create/run but not delete; cleanup code (finally blocks, GC hooks) hitting the stub and leaking billed containers.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/7b8dd433de41f232. Report an issue: GitHub.