Async utilities count swallowed retryer failures as successes and call onSuccess(undefined)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 769
- Forks
- 64
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 3
Description
Describe the bug
AsyncRetryer.execute resolves undefined in three non-success cases: the retryer is disabled (enabled: false), all attempts failed with retryer-level throwOnError: false, or the execution was aborted.
Every async utility that wraps an internal retryer (AsyncDebouncer, AsyncThrottler, AsyncRateLimiter, AsyncQueuer, AsyncBatcher) treats that resolved undefined as a success: successCount increments, lastResult is overwritten with undefined, and onSuccess fires with undefined as the result — e.g. onSuccess: (result: string) => ... receives undefined at runtime.
Repro sketch:
const debouncer = new AsyncDebouncer(
async () => { throw new Error('always fails') },
{
wait: 100,
asyncRetryerOptions: { maxAttempts: 2, throwOnError: false },
onSuccess: (result) => console.log('success?', result), // fires with undefined
},
)
Design questions to settle
- Should a swallowed final failure settle as an error at the parent level (increment
errorCount, fireonError) even though the retryer didn't throw? - Should an aborted execution count as neither success nor error (probably — it already returns early in some paths)?
AsyncRetryer.execute'sundefinedreturn is ambiguous by design; distinguishing outcomes may need an internal result envelope or reading the retryer's final state (lastError) afterexecuteresolves.
Noted during review of #246 (see CodeRabbit feedback there) — split out because the fix changes observable semantics across all five utilities rather than being a bugfix in one.
🤖 Generated with Claude Code
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 at AsyncRetryer.execute and trace how AsyncDebouncer, AsyncThrottler, AsyncRateLimiter, AsyncQueuer, and AsyncBatcher handle its resolved undefined value. Reproduce disabled, swallowed-failure, and aborted executions, then define and verify the intended success, error, callback, and count behavior across all five utilities.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100