Skip to content

fix: correct critical bugs in retry logic#1

Draft
devthejo wants to merge 2 commits into
masterfrom
vibe/fix-critical-bugs-a79551
Draft

fix: correct critical bugs in retry logic#1
devthejo wants to merge 2 commits into
masterfrom
vibe/fix-critical-bugs-a79551

Conversation

@devthejo

@devthejo devthejo commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Robustness hardening for the retry logic, plus two genuine behavior fixes in retry.js. No breaking changes for documented usage.

Behavior fixes (lib/retry.js)

  • retries: Infinity no longer crashes. operation() treats retries === Infinity as the forever signal, but timeouts() used to blow up on it (RangeError: Invalid array length). It now maps Infinity (like null/undefined) to the default base timeout count, which forever mode then cycles through.
  • Non-numeric timeout options fail fast. factor / minTimeout / maxTimeout that aren't numbers previously produced NaN timeouts that silently break setTimeout. They now throw an explicit "<opt> must be a number" at the source instead of being coerced.
  • retries validation rejects negative / NaN values with a clear message (allowing Infinity and null/undefined).

Hardening (no behavior change in normal usage)

  • timeouts() calls exports.createTimeout instead of this.createTimeout, so it keeps working if the function is destructured off the module.
  • createTimeout floors the result at 1ms and guards the exponential term.
  • retry_operation.js: initialize _cachedTimeouts to null in non-forever mode (was undefined — both falsy, cosmetic); nullify _timeout/_timer after clearing; wrap non-Error values via String(err); mainError() falls back to String(error) when message is absent; attempt() catches synchronous throws from the operation fn.
  • index.js: clearer bail() message ("Retry aborted by user").

Testing

  • npm run lint — clean.
  • Integration suite passes. Note: test-retry-operation.js > testRetryForeverNoRetries is timing-flaky on a tight ~150ms window and can intermittently fail on master as well — unrelated to this PR.

Breaking changes

Passing a non-numeric factor/minTimeout/maxTimeout now throws instead of silently returning NaN timeouts. This only affects already-broken configurations.

mistral-vibe and others added 2 commits July 5, 2026 06:49
- Fix randomize option not propagated to retrier.operation in index.js
- Fix this.createTimeout undefined in retry.js (use exports.createTimeout)
- Fix createTimeout returning NaN or 0 for invalid factor/minTimeout
- Fix _cachedTimeouts initialization for non-forever mode
- Fix timer leaks in RetryOperation.retry()
- Fix mainError() handling errors without message property
- Fix synchronous errors not caught in RetryOperation.attempt()
- Fix retries validation to allow null/undefined for forever mode
- Improve error messages for better debugging

All existing tests pass.

Co-authored-by: devthejo <devthejo@users.noreply.github.com>
…ptions

The previous validation threw on retries:Infinity, contradicting
operation() which uses retries === Infinity as its forever signal. Map
Infinity (and null/undefined) to the default base timeout count instead.

Also validate factor/minTimeout/maxTimeout up front: non-numeric values
would silently yield NaN timeouts that break setTimeout. Fail explicitly
instead of coercing, so misconfiguration surfaces at the source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants