MoonshotAI / MoonshotAI/kimi-code
fix(acp-server): surface provider errors on session/prompt
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Problem
When the LLM provider returns a 4xx/5xx/rate-limit/overload/connection-error, the ACP session/prompt call resolves with { stopReason: 'end_turn' } and no JSON-RPC error. The custom ACP client (currently the JetBrains plugin) sees a normal-looking "turn ended, no content" outcome and cannot tell that the model service was actually unavailable.
Repro
- Point the provider config at a custom OpenAI-compatible gateway that returns
500on a chat-completion request. - Run any turn that needs an LLM call.
- Observe the ACP
PromptResponse:{ stopReason: 'end_turn', usage: {...} }. No error. No error code. No retry guidance.
Where the silent resolution happens
packages/acp-server/src/session.ts:907 onTurnEnded:
private onTurnEnded(event): void {
const driver = this.driverFor(event.turnId);
if (driver === undefined) return;
const error = event.error as { code: string; message?: string } | undefined;
this.settleDriver(driver, () => {
if (event.reason === 'failed' && isAuthError(error)) {
driver.reject(RequestError.authRequired(undefined, error?.message));
return;
}
driver.resolve({ stopReason: turnEndReasonToStopReason(event.reason, error) });
});
...
}
The isAuthError(error) check only matches the auth code set (provider.auth_error + auth.*). Anything else with reason: 'failed' falls through to turnEndReasonToStopReason, which only maps provider.filtered to refusal; everything else → end_turn.
Engine side (unchanged)
packages/agent-core-v2/src/kosong/contract/errors.ts already classifies errors as provider.api_error / provider.overloaded / provider.connection_error / provider.rate_limit / context.overflow etc. (born-coded). The loop layer surfaces these as turn.ended { reason: 'failed', error: Error2{ code } }. The classification is correct; the ACP layer just discards it.
Proposed fix
Add a sibling helper to isAuthError in packages/acp-server/src/events-map.ts:
const PROVIDER_ERROR_CODES: ReadonlySet<string> = new Set([
'provider.api_error',
'provider.filtered',
'provider.rate_limit',
'provider.connection_error',
'provider.overloaded',
'provider.not_found',
'context.overflow',
'loop.max_steps_exceeded',
]);
export function isProviderError(error?: { readonly code: string }): boolean {
return error !== undefined && PROVIDER_ERROR_CODES.has(error.code);
}
Extend onTurnEnded in session.ts:
if (event.reason === 'failed' && isAuthError(error)) {
driver.reject(RequestError.authRequired(undefined, error?.message));
return;
}
if (event.reason === 'failed' && isProviderError(error)) {
driver.reject(RequestError.internalError(
{ code: error?.code, message: error?.message, name: error?.name },
error?.message ?? 'model provider reported an error',
));
return;
}
Apply the same logic in mapPromptLaunchError (session.ts:118) so a launch-time failure that already has a provider code surfaces as internalError rather than the fixed 'session prompt failed' text.
Scope note
This change only affects the ACP server. VS Code plugin, TUI, and kap-server all consume the engine directly and are unaffected.
Test plan
packages/acp-server/test/convert.test.ts(or a small newevents-map.test.tsif cleaner): unit-testisProviderErroragainst every code in the set and against codes outside it.packages/acp-server/test/approval.test.ts/interaction-bridge.test.ts: verifymapPromptLaunchErrorreturnsRequestError.internalErrorwhen the launch error has aprovider.*/context.*code, and continues to return'session prompt failed'otherwise.packages/acp-server/test/e2e-turn.test.ts: scripted provider throws aprovider.api_errormid-turn; verify the client receives a JSON-RPC error (not a successfulend_turn); auth path still producesauth_required; unrelated failures still produceend_turn.
Risk
Behaviour change for any external ACP client that today treats a provider outage as "agent produced nothing". Such clients will now see a JSON-RPC error — strictly more informative, no semantic ambiguity, no protocol breakage. Mitigate by announcing the change in apps/kimi-code/CHANGELOG.md.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with isAuthError and the proposed provider-code handling in packages/acp-server/src/events-map.ts and packages/acp-server/src/session.ts, including onTurnEnded and mapPromptLaunchError. Run the named ACP unit and end-to-end tests, then verify provider failures become JSON-RPC errors while auth and unrelated failures retain their existing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100