Skip to content

Background hooks-session-naming call corrupts shared ComputerTool tool version, 400s the root session's next turn #4

Description

@ckrabach617

Symptom

A root session running claude-opus-5 fails an entire turn with an Anthropic 400:

'claude-opus-5' does not support tool types: computer_20250124

Two consecutive attempts, both fail, turn aborts, the user's message is never answered. Observed 2026-08-10 16:42 EDT on WSL2 (Ubuntu on Windows). Installed tree confirmed at cache dir amplifier-bundle-computer-use.git-ca4d97610e35db4c, HEAD eb26715, with upstream fix 9b9d034 confirmed present as an ancestor. This is NOT a recurrence of issue #1 — it survives that fix.

Root Cause (traced end-to-end, file:line)

A background, out-of-band provider call made by hooks-session-naming mutates the SHARED ComputerTool instance's resolved tool version between turns:

  1. hooks-session-naming/__init__.py:37 — session naming defaults to model_role: "fast".
  2. Same file, _call_provider() lines 477–503 — matches the provider by NAME ("anthropic"), so it grabs the very same anthropic provider OBJECT the main session uses. (Verified: active routing matrix is balanced; routing/balanced.yaml:52-56 maps fast → provider anthropic, model claude-haiku-4-5-20251001.)
  3. Lines 533–538 — builds ChatRequest(..., model=model_override, ...) where model_override is the resolved FAST model (a Haiku variant), not the session's claude-opus-5.
  4. Line 549 — awaits provider.complete(). hook-computer-use has already monkey-patched THAT SAME provider object's .complete (hook-computer-use/init.py:829), so this unrelated background call is silently routed through the wrapper too.
  5. hook-computer-use/__init__.py:798–801_effective_model = request.model or provider.default_model. Here request.model IS set (to Haiku), so _effective_model becomes Haiku.
  6. _note_model_on_computer_tool() lines 651–714 — does coordinator.get("tools", "computer") — the SAME shared ComputerTool instance as the main session — and calls tool.note_model(haiku_model).
  7. ComputerTool.note_model() calls resolve_tool_version(): "a known model always wins" unconditionally overwrites _tool_version from computer_20251124 to computer_20250124 (providers.py:269,277 map claude-haiku-4-5 → computer_20250124).
  8. native_tool_spec is read ONCE PER TURN, BEFORE provider.complete() runs. The corruption lands between turns, so the root session's very next turn builds tools[] with the stale computer_20250124 alongside model claude-opus-5 → 400.

Notably, hooks-session-naming's request declares ZERO tools. It has no business influencing the computer tool's resolved version at all.

Why issue #1 fix (9b9d034) doesn't cover it

9b9d034 closed two specific cases:

  • Blind reliance on ChatRequest.model when it's None in sub-agents
  • One-turn-late priming (reading native_tool_spec before complete() runs)

It did not anticipate a same-coordinator, same-provider-object background call from an unrelated hook that explicitly sets request.model to a different model while declaring no tools. The tool_versions.py module docstring already flags this general hazard class ("provider-anthropic's own model-fallback... silently switches models mid-session with no signal to this module"). This is that hazard realized in a different way.

Severity / Blast Radius

It's a race, not a one-off — hooks-session-naming fires on an interval (every 5 turns by default), so it can recur any time that interval lands in the window between turns. Blast radius is any session composing this bundle together with hooks-session-naming and a routing matrix whose fast role resolves to a model in a different computer-tool dialect than the session model. The failure kills the whole turn, not just the tool call.

Suggested Minimal Fix (proposed, not implemented)

In hook-computer-use's wrapped complete(), only call _note_model_on_computer_tool() when the outgoing request actually declares a computer-shaped tool (e.g. request.tools non-empty and containing a computer_* type). A request carrying no tools should never influence the resolved tool version. One-line guard; leaves tool_versions.py's resolution policy untouched.

Recovery

The session self-heals: the wrapper calls _note_model_on_computer_tool() with the correct effective model BEFORE delegating to the real complete(), so the failed attempt itself restores computer_20251124. The turn is lost but the next turn succeeds. Confirmed empirically — the affected session answered normally on the next message with no restart required.

Environment

  • WSL2 (Ubuntu) on Windows
  • Amplifier cache: amplifier-bundle-computer-use.git-ca4d97610e35db4c, HEAD eb26715
  • Routing matrix: balanced (default)
  • Session model: claude-opus-5
  • fast role model: claude-haiku-4-5-20251001
  • Session id: 66b33f1c-41e3-4a62-b619-9a2f599894bf

Reported-by: External WSL2 user (same reporter as issue #1)
Related: Distinct from #1 (fixed by 9b9d034); same module, different corruption path

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions