You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The ACP adapter does not surface a failed turn to the client. When a turn fails for a non-auth reason (e.g. a non-retryable upstream HTTP 400, a rate limit, an expired token after retries are exhausted), packages/acp-adapter/src/session.ts handles turn.ended with reason: 'failed' by calling log.warn(...) and then resolving session/prompt with stopReason: 'end_turn' (only auth-coded failures are routed to a JSON-RPC error via authRequiredFromPayload). The actual error is written to the session log (~/.kimi-code/sessions/.../logs/kimi-code.log) but is not exposed on the ACP wire — not on the JSON-RPC error channel, not as refusal, and not in PromptResponse.
Consequence for ACP orchestration clients (Multica, OpenCode, Claude Code running kimi as a backend): a genuinely failed turn is indistinguishable from a clean, empty completion. The client reports the task as successfully completed with no output, and the actionable error stays buried in the local log.
Concrete case that motivated this report: a session accumulated an empty assistant message, so every subsequent prompt got 400 the message at position N with role 'assistant' must not be empty. kimi acp exited 0 with no output; the orchestrator recorded status=completed, output_bytes=0 on each attempt — a silent no-op the user experienced as "Kimi returns nothing".
Related: #1855 (usage not reported over ACP). Both are ACP-adapter observability gaps; this one is about failure visibility.
What steps can reproduce the bug?
Drive kimi over ACP (kimi acp) from any ACP client.
Force a non-auth, non-retryable turn failure — e.g. resume a session whose history contains an empty assistant message (server returns 400 ... must not be empty), or otherwise trigger a 4xx that exhausts retries.
Observe: session/prompt resolves with stopReason: end_turn, no error is delivered to the client, and the failure appears only in logs/kimi-code.log (WARN llm request failed ... statusCode=400).
What is the expected behavior?
A non-auth turn failure should be observable by the ACP client without reading kimi's local log — e.g. surface the error on the JSON-RPC error channel, or attach the failure payload to PromptResponse (the spec provides _meta for out-of-band data), or otherwise signal that the turn did not complete cleanly. As-is, clients cannot tell a failed turn from an empty successful one.
Additional information
Version: Kimi Code 0.26.0
Source pointer: packages/acp-adapter/src/session.ts, the turn.ended handler — reason === 'failed' non-auth branch resolves { stopReason: turnEndReasonToStopReason(...) } = end_turn, per the documented rationale in events-map.ts (ACP StopReason has no failed variant, so the failure is only logged).
What issue are you seeing?
The ACP adapter does not surface a failed turn to the client. When a turn fails for a non-auth reason (e.g. a non-retryable upstream HTTP 400, a rate limit, an expired token after retries are exhausted),
packages/acp-adapter/src/session.tshandlesturn.endedwithreason: 'failed'by callinglog.warn(...)and then resolvingsession/promptwithstopReason: 'end_turn'(onlyauth-coded failures are routed to a JSON-RPC error viaauthRequiredFromPayload). The actual error is written to the session log (~/.kimi-code/sessions/.../logs/kimi-code.log) but is not exposed on the ACP wire — not on the JSON-RPC error channel, not asrefusal, and not inPromptResponse.Consequence for ACP orchestration clients (Multica, OpenCode, Claude Code running kimi as a backend): a genuinely failed turn is indistinguishable from a clean, empty completion. The client reports the task as successfully completed with no output, and the actionable error stays buried in the local log.
Concrete case that motivated this report: a session accumulated an empty
assistantmessage, so every subsequent prompt got400 the message at position N with role 'assistant' must not be empty.kimi acpexited 0 with no output; the orchestrator recordedstatus=completed, output_bytes=0on each attempt — a silent no-op the user experienced as "Kimi returns nothing".Related: #1855 (usage not reported over ACP). Both are ACP-adapter observability gaps; this one is about failure visibility.
What steps can reproduce the bug?
kimi acp) from any ACP client.assistantmessage (server returns400 ... must not be empty), or otherwise trigger a 4xx that exhausts retries.session/promptresolves withstopReason: end_turn, no error is delivered to the client, and the failure appears only inlogs/kimi-code.log(WARN llm request failed ... statusCode=400).What is the expected behavior?
A non-auth turn failure should be observable by the ACP client without reading kimi's local log — e.g. surface the error on the JSON-RPC error channel, or attach the failure payload to
PromptResponse(the spec provides_metafor out-of-band data), or otherwise signal that the turn did not complete cleanly. As-is, clients cannot tell a failed turn from an empty successful one.Additional information
packages/acp-adapter/src/session.ts, theturn.endedhandler —reason === 'failed'non-auth branch resolves{ stopReason: turnEndReasonToStopReason(...) }=end_turn, per the documented rationale inevents-map.ts(ACPStopReasonhas nofailedvariant, so the failure is only logged).