can1357/oh-my-pi · error · TypeError

tool.{self._name}(...) expects a dict of arguments (got {typ

Error message

tool.{self._name}(...) expects a dict of arguments (got {type(args).__name__})

What it means

`_ToolCallable.__call__` merges an optional positional argument with keyword arguments and requires the result to be a dict, since tool arguments travel as a JSON object over the bridge. This TypeError is raised when the positional argument is not a dict (e.g. a list, string, or kwargs-style pile of non-dict values).

Source

Thrown at packages/coding-agent/src/eval/py/prelude.py:439

    class _ToolCallable:
        """Invokes one host-side tool via the loopback HTTP bridge."""

        __slots__ = ("_name",)

        def __init__(self, name: str):
            self._name = name

        def __repr__(self) -> str:
            return f"<tool.{self._name}>"

        def __call__(self, args=None, /, **kwargs):
            if args is None:
                merged: dict = {}
            elif isinstance(args, dict):
                merged = dict(args)
            else:
                raise TypeError(
                    f"tool.{self._name}(...) expects a dict of arguments (got {type(args).__name__})"
                )
            merged.update(kwargs)
            if INTENT_FIELD not in merged:
                merged[INTENT_FIELD] = "py prelude"
            return _bridge_call(self._name, merged)

    class _ToolProxy:
        """`tool.<name>(args)` proxy mirroring the JS runtime bridge."""

        __slots__ = ()

        def __getattr__(self, name: str) -> _ToolCallable:
            if name.startswith("_"):
                raise AttributeError(name)
            return _ToolCallable(name)

        def __getitem__(self, name: str) -> _ToolCallable:

View on GitHub (pinned to 9690622007)

Solutions

  1. Wrap arguments in a dict keyed by the tool's parameter names: `tool.bash({"command": "ls"})`.
  2. Or use keyword arguments directly: `tool.bash(command="ls")`.
  3. If args come from a variable, ensure it is a dict before calling (`isinstance(args, dict)`).

Example fix

// before
result = tool.bash("ls -la")

// after
result = tool.bash({"command": "ls -la"})
# or equivalently
result = tool.bash(command="ls -la")
Defensive patterns

Strategy: type-guard

Validate before calling

args = {"command": cmd}
if not isinstance(args, dict):
    raise SystemExit("tool args must be a dict")
result = tool.bash(args)

Type guard

def is_tool_args(args) -> bool:
    return isinstance(args, dict)

Try / catch

try:
    result = tool.bash(cmd)
except TypeError as e:
    if "expects a dict of arguments" in str(e):
        result = tool.bash({"command": cmd})

Prevention

When it happens

Trigger: Calling `tool.bash("ls")` instead of `tool.bash({"command": "ls"})`; passing a list like `tool.read_file(["a.txt"])`; positional non-dict values of any type.

Common situations: Porting JS-style calls where args were JSON strings; confusing Python kwargs with the dict form (`tool.bash(command="ls")` is fine via **kwargs, but `tool.bash("ls")` is not); writing helpers that pass through arbitrary user data.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/912a13a23a6e87de. Report an issue: GitHub.