langchain-ai/deepagents · error · ValueError

allow_list must not be empty; disable shell access instead

Error message

allow_list must not be empty; disable shell access instead

What it means

`ShellAllowListMiddleware` validates shell commands against an explicit allow-list without HITL interrupts. An empty `allow_list` is a configuration contradiction: the middleware would reject every command, so the library raises ValueError at construction and tells you to disable shell access instead of running a no-op/deny-all middleware.

Source

Thrown at libs/code/deepagents_code/agent.py:848

    """Omit hook inputs from traces by default; set a `TracePolicy` to override."""

    def __init__(self, allow_list: list[str]) -> None:
        """Initialize with the shell allow-list to validate commands against.

        Args:
            allow_list: Allowed command names (e.g. `["ls", "cat", "grep"]`).
                Must be a non-empty restrictive list — not `SHELL_ALLOW_ALL`.

        Raises:
            ValueError: If `allow_list` is empty.
            TypeError: If `allow_list` is the `SHELL_ALLOW_ALL` sentinel.
        """
        from deepagents_code.config import SHELL_ALLOW_ALL

        super().__init__()
        if not allow_list:
            msg = "allow_list must not be empty; disable shell access instead"
            raise ValueError(msg)
        if isinstance(allow_list, type(SHELL_ALLOW_ALL)):
            msg = (
                "SHELL_ALLOW_ALL should not be used with "
                "ShellAllowListMiddleware; use auto_approve=True instead"
            )
            raise TypeError(msg)
        self._allow_list = list(allow_list)

    def _validate_tool_call(self, request: ToolCallRequest) -> ToolMessage | None:
        """Return an error tool message when a shell command is not allowed.

        Args:
            request: The tool call request being processed.

        Returns:
            An error `ToolMessage` when the shell command should be rejected,
            otherwise `None`.
        """

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Pass a non-empty list of allowed command names, e.g. ShellAllowListMiddleware(["ls", "cat", "grep"]).
  2. If no shell commands should run, don't construct the middleware at all — omit it (or exclude the execute tool) so shell access is disabled.
  3. Check where the list is built (config/env parsing) and handle the empty case before constructing the middleware.

Example fix

// before
middleware = ShellAllowListMiddleware(allow_list=cfg.get("shell_allow", []))
// after
allowed = cfg.get("shell_allow", [])
if not allowed:
    middleware = None  # shell access disabled
else:
    middleware = ShellAllowListMiddleware(allow_list=allowed)
Defensive patterns

Strategy: validation

Validate before calling

allow_list = load_shell_allow_list()  # however you build it
if allow_list:
    middleware = ShellAllowListMiddleware(allow_list=allow_list)
else:
    middleware = None  # shell access disabled

Type guard

def is_valid_allow_list(value: object) -> bool:
    return isinstance(value, list) and len(value) > 0 and all(isinstance(c, str) for c in value)

Try / catch

try:
    mw = ShellAllowListMiddleware(allow_list=allow_list)
except ValueError as e:
    if "allow_list must not be empty" in str(e):
        logger.warning("Empty shell allow-list; disabling shell access")
        mw = None
    else:
        raise

Prevention

When it happens

Trigger: Instantiating `ShellAllowListMiddleware(allow_list=[])` or `ShellAllowListMiddleware(allow_list=some_list)` where `some_list` is an empty list/tuple built at runtime (e.g. from an empty config value or env-parsed list).

Common situations: A config file section like `[shell] allow = []`; an env var parsed into an empty list; code that filters an allow-list down to nothing before constructing the middleware.

Related errors


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