BUG: code_execution_tool raises KeyError: 'code' when the code argument is missing #187

Open
opened 2026-09-09 15:05:30 +00:00 by nsaspy · 0 comments
Owner

Finding

plugins/_code_execution/tools/code_execution_tool.py reads the code argument by direct subscript for the three main runtimes:

if runtime_arg == "python":
    response = await self.execute_python_code(
        cfg, code=self.args["code"], session=session, reset=reset
    )
elif runtime_arg == "nodejs":
    response = await self.execute_nodejs_code(
        cfg, code=self.args["code"], session=session, reset=reset
    )
elif runtime_arg == "terminal":
    response = await self.execute_terminal_command(
        cfg, command=self.args["code"], session=session, reset=reset
    )

Every other argument in the same function is defensive:

runtime_arg = self.args.get("runtime", "").lower().strip()
session = int(self.args.get("session", 0))
self.allow_running = bool(self.args.get("allow_running", False))
reset = bool(self.args.get("reset", False) or runtime_arg == "reset")

Any call with runtime set to python/nodejs/terminal but no code key raises KeyError: 'code' instead of returning a tool result.

Observed traceback from a live run:

File "/a0/plugins/_code_execution/tools/code_execution_tool.py", line 76, in execute
    cfg, command=self.args["code"], session=session, reset=reset
KeyError: 'code'

Impact

The exception is never converted into a corrective tool result. Agent._execute_tool_request has no except around await tool.execute(**tool_args) and Agent.handle_exception() re-raises (if exception: raise exception), so the message-loop task dies. A single malformed tool call (missing arg) kills the whole agent run instead of letting the model retry. A bad runtime value is already handled gracefully via fw.code.runtime_wrong.md; a missing code should behave the same way.

Direction

Minimal fix, consistent with the surrounding style:

code = self.args.get("code", "")

if runtime_arg == "python":
    response = await self.execute_python_code(cfg, code=code, session=session, reset=reset)
elif runtime_arg == "nodejs":
    response = await self.execute_nodejs_code(cfg, code=code, session=session, reset=reset)
elif runtime_arg == "terminal":
    response = await self.execute_terminal_command(cfg, command=code, session=session, reset=reset)

Optional hardening: return a warning fragment (e.g. fw.code.missing_code.md, "'code' argument is required for runtime '{{runtime}}'") when code is absent for the three runtimes, mirroring fw.code.runtime_wrong.md. If implemented, use "code" not in self.args rather than a falsy check — the Input tool forwards code=keyboard where keyboard can legitimately be "" after rstrip(), and a falsy guard would turn that harmless no-op into a warning.

Scope note: the same unguarded subscript exists in upstream agent-zero (inherited unchanged via the plugin-extraction refactor, git blame confirms); the lines are byte-identical in the live runtime copy at /a0/plugins/_code_execution/tools/code_execution_tool.py. A grep across tools/ and plugins/*/tools/ shows no other tool subscripts a required arg without a guard — this is the only instance. Adjacent, out of scope: int(self.args.get("session", 0)) raises ValueError on a non-numeric session.

Acceptance

  • code_execution_tool with {"runtime": "python"}, {"runtime": "nodejs"}, {"runtime": "terminal"} and no code key returns a tool response instead of raising KeyError: 'code'
  • Input tool path with empty keyboard still behaves as a no-op (no false missing-code warning)
  • Regression test added near tests/test_code_execution_pager.py covering the three runtimes without code
  • Terminal/python/nodejs/output/reset smoke paths still pass per plugin DOX

Mirrored from lost-rob0t/a0-symbolics#63 via tracker sync.

## Finding `plugins/_code_execution/tools/code_execution_tool.py` reads the `code` argument by direct subscript for the three main runtimes: ```python if runtime_arg == "python": response = await self.execute_python_code( cfg, code=self.args["code"], session=session, reset=reset ) elif runtime_arg == "nodejs": response = await self.execute_nodejs_code( cfg, code=self.args["code"], session=session, reset=reset ) elif runtime_arg == "terminal": response = await self.execute_terminal_command( cfg, command=self.args["code"], session=session, reset=reset ) ``` Every other argument in the same function is defensive: ```python runtime_arg = self.args.get("runtime", "").lower().strip() session = int(self.args.get("session", 0)) self.allow_running = bool(self.args.get("allow_running", False)) reset = bool(self.args.get("reset", False) or runtime_arg == "reset") ``` Any call with `runtime` set to `python`/`nodejs`/`terminal` but no `code` key raises `KeyError: 'code'` instead of returning a tool result. Observed traceback from a live run: ``` File "/a0/plugins/_code_execution/tools/code_execution_tool.py", line 76, in execute cfg, command=self.args["code"], session=session, reset=reset KeyError: 'code' ``` ## Impact The exception is never converted into a corrective tool result. `Agent._execute_tool_request` has no `except` around `await tool.execute(**tool_args)` and `Agent.handle_exception()` re-raises (`if exception: raise exception`), so the message-loop task dies. A single malformed tool call (missing arg) kills the whole agent run instead of letting the model retry. A bad `runtime` value is already handled gracefully via `fw.code.runtime_wrong.md`; a missing `code` should behave the same way. ## Direction Minimal fix, consistent with the surrounding style: ```python code = self.args.get("code", "") if runtime_arg == "python": response = await self.execute_python_code(cfg, code=code, session=session, reset=reset) elif runtime_arg == "nodejs": response = await self.execute_nodejs_code(cfg, code=code, session=session, reset=reset) elif runtime_arg == "terminal": response = await self.execute_terminal_command(cfg, command=code, session=session, reset=reset) ``` Optional hardening: return a warning fragment (e.g. `fw.code.missing_code.md`, "'code' argument is required for runtime '{{runtime}}'") when `code` is absent for the three runtimes, mirroring `fw.code.runtime_wrong.md`. If implemented, use `"code" not in self.args` rather than a falsy check — the `Input` tool forwards `code=keyboard` where `keyboard` can legitimately be `""` after `rstrip()`, and a falsy guard would turn that harmless no-op into a warning. Scope note: the same unguarded subscript exists in upstream agent-zero (inherited unchanged via the plugin-extraction refactor, `git blame` confirms); the lines are byte-identical in the live runtime copy at `/a0/plugins/_code_execution/tools/code_execution_tool.py`. A grep across `tools/` and `plugins/*/tools/` shows no other tool subscripts a required arg without a guard — this is the only instance. Adjacent, out of scope: `int(self.args.get("session", 0))` raises `ValueError` on a non-numeric `session`. ## Acceptance - [ ] `code_execution_tool` with `{"runtime": "python"}`, `{"runtime": "nodejs"}`, `{"runtime": "terminal"}` and no `code` key returns a tool response instead of raising `KeyError: 'code'` - [ ] `Input` tool path with empty `keyboard` still behaves as a no-op (no false missing-code warning) - [ ] Regression test added near `tests/test_code_execution_pager.py` covering the three runtimes without `code` - [ ] Terminal/python/nodejs/output/reset smoke paths still pass per plugin DOX --- *Mirrored from [`lost-rob0t/a0-symbolics#63`](https://github.com/lost-rob0t/a0-symbolics/issues/63)* via tracker sync.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/a0-symbolics#187
No description provided.