Skip to content

Commit d6e2517

Browse files
committed
fix: use sanitizedRetries consistently in retryOnTransientError
Signed-off-by: leocavalcante <[email protected]>
1 parent 2b143c6 commit d6e2517

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

src/paths.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export async function retryOnTransientError(fn, options = {}) {
202202
const isTransient = isTransientError(err)
203203

204204
// If not a transient error or last attempt, throw immediately
205-
if (!isTransient || attempt === retries) {
205+
if (!isTransient || attempt === sanitizedRetries) {
206206
throw err
207207
}
208208

tests/paths.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,49 @@ describe("paths.mjs exports", () => {
574574
expect(callCount).toBe(1) // Only initial attempt
575575
})
576576

577+
it("should use sanitizedRetries (not raw retries) in last-attempt check", async () => {
578+
// This test verifies the bug fix where `attempt === retries` was used
579+
// instead of `attempt === sanitizedRetries`. With retries: -1, the raw
580+
// comparison would never be true (attempt is never -1), potentially
581+
// causing issues. The sanitized value (0) should be used instead.
582+
let callCount = 0
583+
const start = Date.now()
584+
await expect(
585+
retryOnTransientError(
586+
() => {
587+
callCount++
588+
const err = Object.assign(new Error("EBUSY"), { code: "EBUSY" })
589+
throw err
590+
},
591+
{ retries: -1, initialDelayMs: 50 },
592+
),
593+
).rejects.toThrow("EBUSY")
594+
const elapsed = Date.now() - start
595+
// With sanitizedRetries = 0, only 1 attempt should occur (no retries)
596+
// If the bug existed, it would loop indefinitely or behave incorrectly
597+
expect(callCount).toBe(1)
598+
// Should complete quickly since there are no retries (no delays)
599+
expect(elapsed).toBeLessThan(100)
600+
})
601+
602+
it("should use sanitizedRetries with NaN retries in last-attempt check", async () => {
603+
// When retries is NaN, it should be sanitized to 3 (default)
604+
// The last-attempt check should use sanitizedRetries (3), not NaN
605+
let callCount = 0
606+
await expect(
607+
retryOnTransientError(
608+
() => {
609+
callCount++
610+
const err = Object.assign(new Error("EBUSY"), { code: "EBUSY" })
611+
throw err
612+
},
613+
{ retries: Number.NaN, initialDelayMs: 1 },
614+
),
615+
).rejects.toThrow("EBUSY")
616+
// With sanitizedRetries = 3 (default), expect 4 attempts total
617+
expect(callCount).toBe(4)
618+
})
619+
577620
it("should handle initialDelayMs: 0 (no delay)", async () => {
578621
let callCount = 0
579622
const start = Date.now()

0 commit comments

Comments
 (0)