[Bug] Cancelling retry backoff leaves a referenced timeout alive until the original delay expires
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- node.js, typescript
- Domain
- backend
Research direction
Start by reading packages/agent-core-v2/src/_base/utils/retry.ts, especially sleepForRetry and how it uses sleep and abortable; the report includes a Node.js reproduction that shows the remaining timeout after cancellation. Check the existing tests for this helper and add coverage for cancellation and normal completion. Done means cancellation rejects with the same reason and clears the timeout, while an uncancelled wait still completes after its delay.
Written by the indexing model from the issue text.
Description
What version of Kimi Code is running?
Source-level reproduction at current main 44cdf8f4039e2ff20d3fe8a4e7868bd90fd786e1. The affected retry.ts and abort.ts are byte-identical in the latest release tag @moonshot-ai/[email protected] (commit b5e28fd6beac752a2cdcc0ff2f5fdf40e6494fbb). I tested the real source helper, not an installed CLI build.
Which open platform/subscription and model were you using?
Not applicable: offline CPU reproduction, no provider requests or API key.
What platform is your computer?
macOS arm64, Darwin 25.5.0 arm64 arm, Node.js v24.19.0.
What issue are you seeing?
Cancelling sleepForRetry immediately rejects its Promise with the original cancellation reason, but leaves its referenced timeout alive until the original backoff expires. An otherwise idle Node process therefore cannot exit naturally after the caller has already caught the cancellation.
This is not a claim that cancellation fails to reject, that another provider request is sent, or that the complete CLI/TUI is permanently frozen.
What steps can reproduce the bug?
From the repository root at the commit above, run with Node 24.15 or newer. No dependency installation is needed for these two modules:
node --input-type=module <<'JS'
import { sleepForRetry } from './packages/agent-core-v2/src/_base/utils/retry.ts';
const started = performance.now();
const controller = new AbortController();
const reason = new Error('cancel this retry');
const pending = sleepForRetry(650, controller.signal);
controller.abort(reason);
try {
await pending;
} catch (error) {
console.log('original reason:', error === reason);
}
console.log('active resources:', process.getActiveResourcesInfo());
console.log('cancelled after ms:', Math.round(performance.now() - started));
process.once('beforeExit', () => {
console.log('beforeExit after ms:', Math.round(performance.now() - started));
});
JS
Actual output on my machine (non-timer handles can vary):
original reason: true
active resources: [ 'FSReqCallback', 'PipeWrap', 'PipeWrap', 'Timeout' ]
cancelled after ms: 1
beforeExit after ms: 651
What is the expected behavior?
After cancellation, reject promptly with the same reason and clear the pending timeout. With no remaining work, the process should be able to exit without waiting for the cancelled delay. Normal, non-cancelled waits should still complete after their delay.
Additional information
- The helper creates
sleep(delayMs)before racing it withabortable;sleepdiscards the timer handle. Rejecting the outer Promise does not cancel that timer. - Actual callers include full-compaction retries and the opt-in infinite LLM retry path. The issue is the leftover resource, not whether those callers stop retrying.
- In three fresh control runs, cancellation immediately and one event-loop turn after starting 500ms, 32s and 60s sleeps consistently left one active timeout per cancelled wait. Pre-aborted signals created none; normal completion cleared the timer. Long-delay fixtures explicitly cleaned up the observed handles after measurement.
- Independent child processes rejected immediately but stayed alive for about 650ms in all runs. Pre-aborted controls and a test-only control that clears the captured timer on abort exited immediately. Native
node:timers/promisescancellation also left no pending timer. - No repository implementation was changed. No live inference, complete CLI/TUI lifecycle, Windows/Linux, or full-repository test suite was exercised.
- Related reports #1796, #1860 and #2025 concern rate-limit handling/visibility before cancellation. This reproduces after cancellation has already been caught, without HTTP or provider errors.
- #2855 addresses delayed cancellation inside provider SDK retries; rejection here is already immediate. The CLI also has a force-exit fallback from #1233, so I am not claiming that every
kimi -prun hangs. This report is specifically about the engine helper retaining a referenced timer after cancellation.
Contribution
- I am willing to submit a PR for this bug fix myself (please wait for maintainer approval in this issue first)
- Dominant language
- TypeScript
- Stars
- 7.9k
- Forks
- 1.3k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 98
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing 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.
More from MoonshotAI/kimi-code
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
MoonshotAI/kimi-code#4315 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 60/100
MoonshotAI/kimi-code#4225 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
MoonshotAI/kimi-code#4177 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
MoonshotAI/kimi-code#4170 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
MoonshotAI/kimi-code#4147 · 1 comment ·
Maintainers usually reply within 1 day
All issues in MoonshotAI/kimi-code
Similar issues
-
area:agent-view area:tui bug platform:macos
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
anthropics/claude-code#104558 ·
Maintainers usually reply within 1 day
-
updater
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
UniversityOfHelsinkiCS/oodikone#5141 ·
-
maintanance
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
inmanta/web-console#7606 ·
Maintainers usually reply within 1 day
-
bug skills
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
equinor/fusion-framework#5846 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 74/100