Skip to content

bug(api): POST /api/agent/execute_command cannot be called by any client — Form and dict body cannot both be satisfied #15527

Description

@mrveiss

What

POST /api/agent/execute_command (autobot-backend/api/agent.py:836) cannot be called
successfully by any client. Its signature is:

async def execute_command(
    request: Request,
    command_data: dict,
    user_role: str = Form("user"),
    admin_check: bool = Depends(check_admin_permission),
):

The Form default makes FastAPI read the whole body as form data, and a form field
arrives as a string, so command_data: dict can never validate. FastAPI publishes the
operation as application/x-www-form-urlencoded with command_data required — a
combination nothing can satisfy.

Measured

Mounted through the router registry with check_admin_permission overridden so body parsing
is what is observed, every candidate shape answers 422:

json {"command": "ls", "session_id": "s1"}            -> 422 missing        body.command_data
json {"command_data": {...}, "user_role": "user"}     -> 422 missing        body.command_data
form command_data='{"command": "ls"}', user_role=user -> 422 dict_type      body.command_data
form command_data=ls, user_role=user                  -> 422 dict_type      body.command_data

Why it matters

autobot_sdk.resources.agents.send_command and the TypeScript SDK's sendCommand both
target this route. Neither can be corrected client-side, because there is no correct body.
PR #15526 therefore records the row as unsatisfiable in
repo_tests/sdk_request_body_test.py rather than inventing a body — and that entry is
checked to still be necessary, so fixing this route makes that guard fail and the entry
comes out with it.

Acceptance criteria

  • POST /api/agent/execute_command accepts a body a client can construct — one request
    model rather than a dict body parameter mixed with a Form field
  • admin_check and user_role semantics are unchanged (this is a parsing fix, not an
    authorization change)
  • autobot_sdk.resources.agents.send_command sends that body, and its
    _UNSATISFIABLE entry is removed from repo_tests/sdk_request_body_test.py
  • The same fix reaches libs/autobot-sdk-ts's sendCommand

Found while delivering #15057. Refs #15057, #15526.

Activity

  1. mrveiss commented on Sep 2, 2026

    @mrveiss
    OwnerAuthor

    Delivered in PR #15541. Refs, not Closes — three of four acceptance criteria are met and
    the fourth cannot be done from Dev_new_gui.

    AC State Evidence
    Accepts a body a client can construct — one request model, not dict + Form met api/schemas_agent_requests.py::CommandExecutePayload; execute_command(request, payload, admin_check) in api/agent.py. Measured: before: media=['application/x-www-form-urlencoded'] / after: media=['application/json'], and POST json {"command": "ls", "session_id": "s1"} moves from 422 missing body.command_data to 200.
    admin_check and user_role semantics unchanged met admin_check: bool = Depends(check_admin_permission) untouched. user_role is a model field with the same "user" default: absent -> 200 {"user_role":"user"}, supplied -> 200 {"user_role":"admin"}.
    send_command sends that body; its _UNSATISFIABLE entry is removed from repo_tests/sdk_request_body_test.py half met The SDK now posts {"command": command} and drops session_id, which named nothing the route has. repo_tests/sdk_request_body_test.py does not exist on Dev_new_gui — it is on the unmerged PR #15526, and the entry is checked to still be necessary, so it must come out when that PR rebases onto #15541. Flagged there.
    The same fix reaches libs/autobot-sdk-ts's sendCommand met sendCommand(command) posts { command }.

    Also added, for the class rather than the instance:
    repo_tests/route_form_and_json_body_are_not_mixed_test.py. It sweeps 2791 route handlers
    across both backends and autobot_shared, of which 24 declare a Form field, and asserts
    none of those also declares a JSON body parameter. This route was the only violation on the
    tree; it is now zero. Three mutations are recorded on the PR, including one that misspells
    the Form token and proves the sweep is matching a token the source actually emits.

    Close this once #15526 lands with the _UNSATISFIABLE entry removed.

  2. added this to the v0.9.0 milestone on Sep 12, 2026
  3. added a commit that references this issue on Sep 12, 2026
  4. mrveiss commented on Sep 17, 2026

    @mrveiss
    OwnerAuthor

    Closure check against origin/main (360e08e). All 4 ACs verified independently, not assumed from the earlier comment.

    AC Status Evidence
    One request model, no dict + Form mix Met autobot-backend/api/agent.py:845-847 — execute_command(request, payload: CommandExecutePayload, admin_check). CommandExecutePayload (api/schemas_agent_requests.py:60-64): command: str, user_role: str = "user".
    admin_check/user_role semantics unchanged Met admin_check: bool = Depends(check_admin_permission) untouched; user_role keeps its "user" default, read at agent.py:871 as payload.user_role.
    send_command sends that body; _UNSATISFIABLE entry removed Met libs/autobot-sdk-python/autobot_sdk/resources/agents.py:50-59 posts {"command": command}, drops session_id. repo_tests/sdk_request_body_test.py — grepped directly, no _UNSATISFIABLE string present.
    TS SDK's sendCommand gets the same fix Met libs/autobot-sdk-ts/src/resources/agents.ts:48 — sendCommand(command: string).

    Delivered across #15526 (recorded then removed the unsatisfiable test entry) and #15541 (the actual route/SDK fix, merged 2026-09-03). Both merged; the earlier comment's "cannot be done from Dev_new_gui" was a same-day sequencing note (PR #15526 landed hours after that comment), not a rename issue — confirmed by reading the merge timestamps directly.

    Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions