Two rejections from the tool_call bridge fuelled identical-retry loops on
small models because they stated a constraint without a correction:
* A multi-entry batch naming a local tool got "Local tools require one entry
per tool_call; mixed and multi-local batches are not supported." A 9B model
re-sent the same two-entry array until identical_call_streak_halt. The
error now restates the valid shape with the caller's OWN first entry
("you sent 2. Retry with only: {"calls":[<first entry>]} then issue the
remaining 1 call(s) as separate tool_call invocations"), which is the
hand-written correction that unstuck the reporter's session. The refusal
itself stays: b4d04eb8fd keeps local entries out of batches deliberately
(a batch bypasses per-tool admission, path-overlap serialization and the
per-server MCP parallel opt-in). Shared helper local_batch_error() feeds
both resolve_underlying_call and the connector dispatcher's guard.
* "'<name>' is not a deferrable tool. If it appears in the model-facing
tools list already, call it directly" fired for two opposite mistakes.
For a directly-listed tool the advice is right; for an unknown name —
typically a deferred MCP tool cited by its bare suffix
('mempalace_search' for 'mcp__mempalace__mempalace_search') — "call it
directly" is the opposite of what the model must do. not_deferrable_error()
now distinguishes them, suggests the registered full name when one ends
with '__<name>', and points at tool_search otherwise. tool_describe's
wrong-door error uses the same helper.
Also ports one parametrize row from #114488 (a stringified single object
without a name flows into the legacy single-shape tolerance) and adds two
invariant tests; docs updated.
Co-authored-by: Konstantin Khlopkov <konstantin.khlopkov93@gmail.com>
Co-authored-by: KoNit-K <konit.block@protonmail.com>