Skip to content

Managed k3 stream emits corrupted tool names with literal '?' fragments; engine should validate unknown tool names #3230

Description

@sachnelson

Summary

Since ~Aug 24, tool calls over managed:kimi-code (k3) intermittently arrive with corrupted names streamed in fragments — e.g. mcp__linear__list_issu?list_issues (a literal ?, 0x3F, mid-name). The engine rejects them (Tool "..." not found), the model retries the identical garbage, and the turn stalls.

This is distinct from #3220's parser defect: there, the client dropped name fragments; here, the bytes arriving from the API are themselves malformed — verified by reassembling the stream with a patched parser and hex-dumping the recorded tool call. The corruption is on the wire, not introduced client-side.

Environment

  • kimi 0.36.0 (released Aug 13) and a source build of main @ 41f1eae (0.38.0); Windows 11
  • Provider managed:kimi-code, model k3, thinking effort high
  • MCP: 8 servers, ~130 tools; failures observed on the Linear http server (55 tools, long shared mcp__linear__ prefix)

Evidence

Session wire (~/.kimi-code/sessions/<wd>/<session>/agents/main/wire.jsonl):

{"type":"tool.call","name":"mcp__linear__list_issu?list_issues","args":{}}
{"type":"tool.call","name":"mcp__linear__get_issu?get_issue","args":{}}

Why this needs a server-side fix

No client parser can reconstruct intent from bytes that are wrong at the source. The model clearly intends mcp__linear__list_issues; the stream delivers mcp__linear__list_issu?list_issues.

What the client could still do (proposal)

Even with a server fix, the engine currently treats these as ordinary tool results (Tool "..." not found) and burns the step budget until the turn dies. Two complementary guards, aligned with #3220's "Suggested handling":

  1. Classify an unknown-tool-name call as a retryable step failure so loopControl.maxAttemptsPerStep engages, instead of silently advancing.
  2. Optionally, constrained name recovery: when a name is unknown, sanitize it (split on non-[A-Za-z0-9_]) and rewrite to a catalog tool only on a unique match — exact segment, then _-bounded suffix, then prefix, in that priority. Ambiguous or unmatched names keep the current rejection. In local testing this rescued every corrupted call observed (list_issu?list_issueslist_issues, get_issu?get_issueget_issue) while correctly rejecting ambiguous truncations (list_issu alone matches 3 catalog tools → rejected).

I have both the #3220 parser patch and this recovery guard implemented with unit tests in a local fork (suites green, typecheck/lint clean) and can contribute either under an /approve. Per the contributing guide, behavior changes are maintainer-gated — so treat this as the discussion issue first.

Reproduction

With a large MCP catalog (Linear's 55-tool http server makes it frequent), start a fresh session and ask the agent to list or review Linear issues. Intermittent — but when it triggers, it repeats within the session. Happy to attach a kimi export debug zip if useful.

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