langchain-ai/deepagents · error · ValueError

shell_allow_list must be None or non-empty

Error message

shell_allow_list must be None or non-empty

What it means

`ServerConfig.__post_init__` enforces that `shell_allow_list` is either `None` (meaning unrestricted/default behavior) or a non-empty list of shell commands. An explicitly provided empty list `[]` is ambiguous and therefore rejected with this `ValueError` at construction time.

Source

Thrown at libs/code/deepagents_code/_server_config.py:483

        values.pop("PROJECT_ROOT")
        serialized = json.dumps(values, sort_keys=True, separators=(",", ":"))
        return hashlib.sha256(serialized.encode()).hexdigest()

    def __post_init__(self) -> None:
        """Normalize fields and validate invariants.

        Raises:
            TypeError: If `rubric_max_iterations` or `recursion_limit` is a
                boolean.
            ValueError: If `shell_allow_list` is an empty list,
                `allow_fs_tools` is an empty list or omits `"read_file"`, or
                `rubric_max_iterations` / `recursion_limit` is non-positive.
        """
        if self.sandbox_type == "none":
            object.__setattr__(self, "sandbox_type", None)
        if self.shell_allow_list is not None and len(self.shell_allow_list) == 0:
            msg = "shell_allow_list must be None or non-empty"
            raise ValueError(msg)
        # `allow_fs_tools` is a security control: `None` means unrestricted, but
        # an explicit list must be a usable allowlist. Own the non-empty +
        # `read_file`-required invariant here (the single authoritative point
        # for both the env round-trip via `from_env` and direct construction)
        # rather than deferring to `FilesystemMiddleware`, which would only
        # surface the violation a process boundary away. `_parse_allow_fs_tools_flag`
        # still enforces the same rule at the CLI for a friendlier error.
        if self.allow_fs_tools is not None:
            if len(self.allow_fs_tools) == 0:
                msg = "allow_fs_tools must be None or a non-empty list"
                raise ValueError(msg)
            if "read_file" not in self.allow_fs_tools:
                msg = "allow_fs_tools must include 'read_file'"
                raise ValueError(msg)
        if isinstance(self.rubric_max_iterations, bool):
            msg = "rubric_max_iterations must be None or a positive integer"
            raise TypeError(msg)
        if self.rubric_max_iterations is not None and self.rubric_max_iterations <= 0:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Pass `shell_allow_list=None` if you intend unrestricted/default shell behavior
  2. Populate the list with at least one allowed command, e.g. shell_allow_list=["ls","grep"]
  3. Fix upstream code so a fully-filtered list collapses to `None` instead of `[]`
  4. If the value comes from the environment, unset `DEEPAGENTS_CODE_SERVER_SHELL_ALLOW_LIST` rather than setting it empty

Example fix

// before
config = ServerConfig(shell_allow_list=[], ...)
# ValueError: shell_allow_list must be None or non-empty
// after
config = ServerConfig(shell_allow_list=None, ...)  # or ["ls", "cat"]
Defensive patterns

Strategy: validation

Validate before calling

def coerce_shell_allow_list(cmds: list[str] | None) -> list[str] | None:
    return cmds if cmds else None  # empty list collapses to None before constructing ServerConfig

Type guard

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

Try / catch

try:
    config = ServerConfig(**kwargs)
except ValueError as e:
    if "shell_allow_list" in str(e):
        print(f"Invalid shell_allow_list: {e}; pass None or a non-empty list")
        sys.exit(2)
    raise

Prevention

When it happens

Trigger: Constructing `ServerConfig` (directly or via `from_env`/`from_cli_args`) with `shell_allow_list=[]`, or setting `DEEPAGENTS_CODE_SERVER_SHELL_ALLOW_LIST` such that it round-trips to an empty list.

Common situations: Code that starts with a default list, filters it, and ends up empty before passing it in; env/CLI parsing that produces `[]` from an empty or blank value; template configs rendering an empty placeholder.

Related errors


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