can1357/oh-my-pi · error · AttributeError
name
Error message
name
What it means
The `tool` proxy raises AttributeError for any attribute access starting with `_`, reserving underscore-prefixed names for Python internals and preventing them from being mistaken for tool calls. Accessing `tool._something` (or Python's own dunder probing like `tool.__deepcopy__`) hits this path instead of creating a `_ToolCallable`.
Source
Thrown at packages/coding-agent/src/eval/py/prelude.py:454
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:
return _ToolCallable(name)
def __repr__(self) -> str:
session = os.environ.get("PI_TOOL_BRIDGE_SESSION")
return (
f"<tool proxy session={session}>"
if session
else "<tool proxy unavailable>"
)
tool = _ToolProxy()
def completion(prompt, *, model="default", system=None, schema=None):
"""Oneshot, stateless completion against a model tier.
View on GitHub (pinned to 9690622007)
Solutions
- Drop the leading underscore and call the tool by its real public name: `tool.read_file(...)`.
- If you need a tool whose name genuinely starts with `_`, use the item form which bypasses the guard: `tool["_name"](...)`.
- Don't try to access Python-internal attributes on the proxy; it intentionally exposes only tool-callable names.
Example fix
// before
private = tool._debug_state() # AttributeError
// after
if tool_name.startswith("_"):
private = tool[tool_name]() # item access bypasses the underscore guard
else:
private = getattr(tool, tool_name)() Defensive patterns
Strategy: try-catch
Validate before calling
name = "_debug"
if name.startswith("_"):
print("underscore names are reserved; use tool[name] item access for such tools") Try / catch
try:
callable_ = getattr(tool, name)
except AttributeError:
if name.startswith("_"):
callable_ = tool[name] # item access bypasses the underscore guard
else:
raise Prevention
- Treat `tool` as a call-by-public-name proxy only; don't reach for private attributes on it.
- For tools whose names start with underscore, use the subscript form tool["_name"](...).
- Expect framework dunder probes (`__deepcopy__`, `_repr_html_`) to raise; register the proxy with copy/serialization hooks explicitly if needed.
When it happens
Trigger: Accessing `tool._internal`, calling `hasattr(tool, "_x")`, or any protocol probe that looks up `__<name>__` / `_<name>` attributes on the proxy.
Common situations: Accidentally treating `tool` as a module and reaching for private helpers; serialization/copy frameworks probing dunder methods (`__getstate__`, `_repr_html_`); an IDE autocompletion exploring underscore names.
Related errors
- tool.{self._name}(...) expects a dict of arguments (got {typ
- No messages to continue from
- Cannot continue from message role: assistant
- Received text_delta for non-text content
- Received text_end for non-text content
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8491b5dea1659a12.
Report an issue: GitHub.