google/zx · error · Fail
Callback is required for retry
Error message
Callback is required for retry
What it means
Thrown by retry() when no callback function is provided. With the 2-arg overload retry(count, cb), the 2nd argument must be a function; with the 3-arg overload retry(count, duration, cb), the 3rd must be a function. If the final positional argument is not a function (e.g. only count and a duration were passed, or the callback was dropped), retry refuses to proceed.
Source
Thrown at src/goods.ts:217
for await (const chunk of stream.setEncoding('utf8')) {
buf += chunk
}
return buf
}
export async function retry<T>(count: number, callback: () => T): Promise<T>
export async function retry<T>(
count: number,
duration: Duration | Generator<number>,
callback: () => T
): Promise<T>
export async function retry<T>(
count: number,
d: Duration | Generator<number> | (() => T),
cb?: () => T
): Promise<T> {
if (typeof d === 'function') return retry(count, 0, d)
if (!cb) throw new Fail('Callback is required for retry')
const total = count
const gen =
typeof d === 'object'
? d
: (function* (d) {
while (true) yield d
})(parseDuration(d))
let attempt = 0
let lastErr: unknown
while (count-- > 0) {
attempt++
try {
return await cb()
} catch (err) {
lastErr = err
const delay = gen.next().valueView on GitHub (pinned to 00a2c484e2)
Solutions
- Pass the callback as the final argument: `retry(3, () => fetch(url))`.
- With a delay, use the 3-arg form: `retry(3, '100ms', () => doThing())`.
- Double-check overload order: (count, [duration | generator], callback).
Example fix
// before retry(3, '100ms') // after retry(3, '100ms', () => doThing())
Defensive patterns
Strategy: type-guard
Validate before calling
function validateRetryArgs(count: number, d: unknown, cb?: unknown): void {
const fn = typeof d === 'function' ? d : cb
if (typeof fn !== 'function') {
throw new TypeError('retry: a callback function is required')
}
}
validateRetryArgs(3, '100ms', cb) Type guard
const isCallback = (v: unknown): v is (...a: any[]) => any => typeof v === 'function'
Try / catch
import { Fail } from 'zx'
try {
await retry(3, maybeDuration)
} catch (e) {
if (e instanceof Fail && /Callback is required for retry/.test(e.message)) {
throw new TypeError('retry(count, [duration], callback) — callback missing')
}
throw e
} Prevention
- Always pass the callback as the final positional argument.
- Remember the overload order: (count, [duration | generator], callback).
- Use TypeScript overloads to catch missing callbacks at compile time.
When it happens
Trigger: `retry(3)`; `retry(3, '100ms')` (duration but no callback); passing an options object instead of a function; a refactor that removed the callback.
Common situations: Refactoring that dropped the callback; misreading the overload signature; passing a non-function placeholder.
Related errors
- Malformed command at ${$.from}
- Inappropriate usage. Apply $ instead of direct instantiation
- Invalid duration: "${d}".
AI-assisted analysis of google/zx@00a2c484e2 (2026-08-13).
Data as JSON: /api/errors/31b8add7eee4e519.
Report an issue: GitHub.