Skip to content

Commit 8ef7d9a

Browse files
committed
fix: sanitize retries option in retryOnTransientError
Signed-off-by: leocavalcante <[email protected]>
1 parent e761c46 commit 8ef7d9a

File tree

2 files changed

+14
-14
lines changed

2 files changed

+14
-14
lines changed

src/paths.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,11 @@ export async function retryOnTransientError(fn, options = {}) {
181181
const sanitizedDelayMs = Number.isNaN(initialDelayMs)
182182
? 100
183183
: Math.max(0, initialDelayMs)
184+
// Sanitize retries: clamp negative to 0, handle NaN by using default
185+
const sanitizedRetries = Number.isNaN(retries) ? 3 : Math.max(0, retries)
184186
let lastError
185187

186-
for (let attempt = 0; attempt <= retries; attempt++) {
188+
for (let attempt = 0; attempt <= sanitizedRetries; attempt++) {
187189
try {
188190
return await fn()
189191
} catch (err) {

tests/paths.test.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -543,10 +543,9 @@ describe("paths.mjs exports", () => {
543543
expect(callCount).toBe(1) // Only initial attempt, no retries
544544
})
545545

546-
it("should throw undefined when retries is negative (loop never executes)", async () => {
546+
it("should clamp negative retries to 0 (only initial attempt)", async () => {
547547
let callCount = 0
548-
// With retries: -1, loop condition (0 <= -1) is false, so loop never runs
549-
// lastError is undefined, so it throws undefined
548+
// With retries: -1, sanitized to 0, so only initial attempt runs
550549
await expect(
551550
retryOnTransientError(
552551
() => {
@@ -556,11 +555,11 @@ describe("paths.mjs exports", () => {
556555
},
557556
{ retries: -1 },
558557
),
559-
).rejects.toBeUndefined()
560-
expect(callCount).toBe(0) // Loop never runs
558+
).rejects.toThrow("EAGAIN")
559+
expect(callCount).toBe(1) // Only initial attempt
561560
})
562561

563-
it("should throw undefined when retries is -5 (loop never executes)", async () => {
562+
it("should clamp retries -5 to 0 (only initial attempt)", async () => {
564563
let callCount = 0
565564
await expect(
566565
retryOnTransientError(
@@ -571,8 +570,8 @@ describe("paths.mjs exports", () => {
571570
},
572571
{ retries: -5 },
573572
),
574-
).rejects.toBeUndefined()
575-
expect(callCount).toBe(0) // Loop never runs
573+
).rejects.toThrow("EAGAIN")
574+
expect(callCount).toBe(1) // Only initial attempt
576575
})
577576

578577
it("should handle initialDelayMs: 0 (no delay)", async () => {
@@ -615,10 +614,9 @@ describe("paths.mjs exports", () => {
615614
expect(elapsed).toBeLessThan(50)
616615
})
617616

618-
it("should throw undefined when retries is NaN (loop never executes)", async () => {
617+
it("should handle NaN retries (falls back to default 3)", async () => {
619618
let callCount = 0
620-
// When retries is NaN, the loop condition (attempt <= retries) is always false
621-
// So the loop never runs and lastError (undefined) is thrown
619+
// When retries is NaN, it is sanitized to the default 3 retries
622620
await expect(
623621
retryOnTransientError(
624622
() => {
@@ -628,8 +626,8 @@ describe("paths.mjs exports", () => {
628626
},
629627
{ retries: Number.NaN },
630628
),
631-
).rejects.toBeUndefined()
632-
expect(callCount).toBe(0) // Loop never runs
629+
).rejects.toThrow("EAGAIN")
630+
expect(callCount).toBe(4) // Initial + 3 retries (default)
633631
})
634632

635633
it("should handle non-numeric initialDelayMs (NaN falls back to default 100ms)", async () => {

0 commit comments

Comments
 (0)