google/zx · error · Error
Invalid duration: "${d}".
Error message
Invalid duration: "${d}". What it means
Thrown by parseDuration() when the input is a number that is NaN or negative. Durations must be a non-negative finite number, or a string matching /^(\d+)(m?s?)$/ (e.g. '100ms', '2s', '3m'). parseDuration backs sleep(), retry() delays, expBackoff(), and $.timeout, so any of those surfaces can surface this error. Note: this is thrown as a plain `new Error`, not a zx Fail instance.
Source
Thrown at src/util.ts:106
.replace(/\v/g, '\\v')
.replace(/\0/g, '\\0') +
`'`
)
}
export function quotePowerShell(arg: string): string {
if (arg === '') return `''`
if (/^[\w/.\-@:=,+%]+$/.test(arg)) return arg
return `'` + arg.replace(/'/g, "''") + `'`
}
export type Duration =
number | `${number}` | `${number}m` | `${number}s` | `${number}ms`
export function parseDuration(d: Duration) {
if (typeof d === 'number') {
if (isNaN(d) || d < 0) throw new Error(`Invalid duration: "${d}".`)
return d
}
const [m, v, u] = d.match(/^(\d+)(m?s?)$/) || []
if (!m) throw new Error(`Unknown duration: "${d}".`)
return +v * ({ s: 1000, ms: 1, m: 60_000 }[u] || 1)
}
export const once = <T extends (...args: any[]) => any>(fn: T) => {
let called = false
let result: ReturnType<T>
return (...args: Parameters<T>): ReturnType<T> =>
called ? result : ((called = true), (result = fn(...args)))
}
export const proxyOverride = <T extends object>(
origin: T,View on GitHub (pinned to 00a2c484e2)
Solutions
- Validate before passing: `if (Number.isFinite(d) && d >= 0) ...`.
- Clamp to a safe minimum: `Math.max(0, d)`.
- Prefer the explicit string form ('100ms', '2s') for readability and to avoid NaN.
- Guard config-driven values and treat -1/NaN as 'no timeout'.
Example fix
// before $.timeout = rawTimeout // rawTimeout may be -1 or NaN // after $.timeout = Number.isFinite(rawTimeout) && rawTimeout >= 0 ? rawTimeout : undefined
Defensive patterns
Strategy: validation
Validate before calling
import { parseDuration } from 'zx'
function safeDuration(d: number | string | undefined): number | undefined {
if (d == null) return undefined
const n = typeof d === 'number' ? d : Number(d)
if (!Number.isFinite(n) || n < 0) return undefined // treat as 'no duration'
return parseDuration(d as any)
}
const delay = safeDuration(raw) Type guard
const isValidDurationNumber = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v >= 0
Try / catch
try {
await sleep(duration)
} catch (e) {
if (e instanceof Error && /Invalid duration/.test(e.message)) {
// skip the wait; duration was NaN/negative
return
}
throw e
} Prevention
- Validate duration with Number.isFinite(d) && d >= 0 before passing to sleep/retry/timeout.
- Clamp computed deltas with Math.max(0, delta) to avoid negatives.
- Prefer the string form ('100ms', '2s') over numeric inputs derived from untrusted sources.
When it happens
Trigger: `sleep(-1)`; `$.timeout = -100`; `retry(3, NaN, fn)`; `parseDuration(Number('abc'))` (NaN); `expBackoff(-5)`; a computed duration that underflows to negative.
Common situations: Subtracting timestamps that yield a negative delta; parseFloat on malformed input producing NaN; config that uses -1 to mean 'disabled' fed directly into zx; overflow/underflow in delay math.
Related errors
AI-assisted analysis of google/zx@00a2c484e2 (2026-08-13).
Data as JSON: /api/errors/fb1e0d1a2dab8f27.
Report an issue: GitHub.