Skip to content

[ACP] Non-auth provider failures such as quota 403 are swallowed as end_turn #1813

Description

@lodystage

What version of Kimi Code is running?

0.26.0 (@moonshot-ai/kimi-code@0.26.0, commit 36b05820cba24e09fdff19a059afc08ccea2c35e)

Which open platform/subscription were you using?

Kimi Code managed OAuth subscription with a billing-cycle usage quota.

Which model were you using?

kimi-code/k3

What platform is your computer?

Darwin 25.5.0 arm64 arm

What issue are you seeing?

When a provider request fails because the billing-cycle usage quota is exhausted, Kimi Code detects and logs the failure internally, but its ACP adapter resolves session/prompt successfully with stopReason: "end_turn".

The ACP client therefore receives no error and cannot tell the user that quota was exhausted. From the client side, the turn looks like an empty successful completion or a stalled conversation.

This is a quota failure, not an authentication or permission failure.

Internal Kimi Code log:

llm request failed ... errorName=APIStatusError errorMessage="403 You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle." statusCode=403

acp: turn ended with failed reason error={"code":"provider.api_error","name":"APIStatusError","details":{"statusCode":403,"turnId":2},"retryable":false}

ACP-observed result:

{
  "stopReason": "end_turn"
}

No machine-readable provider or quota error reaches the ACP client.

What steps can reproduce the bug?

  1. Log in to Kimi Code using a quota-based managed subscription.
  2. Start Kimi Code as an ACP agent with kimi acp and connect an ACP client.
  3. Exhaust the billing-cycle quota.
  4. Send another prompt through the ACP client.
  5. Compare the Kimi Code log with the session/prompt JSON-RPC result.

Kimi Code logs a turn.ended event with reason: "failed" and a non-auth provider.api_error, while the ACP request resolves normally as end_turn. Repeating the prompt produces the same result.

What is the expected behavior?

A turn.ended event with reason: "failed" should be surfaced through the ACP/JSON-RPC error channel, with safe machine-readable fields such as the provider error code, HTTP status, and retryability. The ACP client should be able to display the quota message and mark the turn as failed.

It should not be converted into a successful end_turn. Authentication failures can keep their existing authRequired handling.

Additional information

The behavior appears to be explicitly implemented and covered by a test:

The same path is still present on the current main branch. I could not find an existing issue that combines quota exhaustion with the ACP failure being swallowed as end_turn.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions