Skip to content

Async utilities count swallowed retryer failures as successes and call onSuccess(undefined) #247

Description

@KevinVandy

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, fire onError) 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's undefined return is ambiguous by design; distinguishing outcomes may need an internal result envelope or reading the retryer's final state (lastError) after execute resolves.

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions