cube-js/cube · error · Error

Option times in asyncRetry, must be a positive integer

Error message

Option times in asyncRetry, must be a positive integer

What it means

asyncRetry() validates its options up front: options.times must be a positive integer (> 0). Passing 0, a negative number, or a non-numeric value throws this error immediately instead of attempting any retries. It is a fail-fast contract error so misconfiguration is caught before fn is ever invoked.

Source

Thrown at packages/cubejs-backend-shared/src/promises.ts:459

  call.release = refreshInterval.cancel;

  return call;
};

export type RetryOptions = {
  times: number,
};

/**
 * High order function that do retry when async function throw an exception
 */
export const asyncRetry = async <Ret>(
  fn: () => Promise<Ret>,
  options: RetryOptions
) => {
  if (options.times <= 0) {
    throw new Error('Option times in asyncRetry, must be a positive integer');
  }

  let latestException: unknown = null;

  for (let i = 0; i < options.times; i++) {
    try {
      return await fn();
    } catch (e) {
      latestException = e;
    }
  }

  throw latestException;
};

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Pass a positive integer for options.times (e.g. 3 or 5).
  2. Coerce and validate config before calling: Number.isInteger(times) && times > 0, else fall back to a sane default.
  3. If 'no retries' is desired, call fn directly instead of asyncRetry (times must still be >= 1).
  4. Fix the env/config parsing that produced the invalid value (e.g. `const times = parseInt(v, 10) || 3;`).

Example fix

// before
await asyncRetry(fn, { times: process.env.RETRY_TIMES as any });
// after
const times = parseInt(process.env.RETRY_TIMES ?? '3', 10);
await asyncRetry(fn, { times: Number.isInteger(times) && times > 0 ? times : 3 });
Defensive patterns

Strategy: validation

Validate before calling

function assertTimes(times: unknown): asserts times is number {
  if (!Number.isInteger(times) || (times as number) <= 0) {
    throw new Error('Option times in asyncRetry, must be a positive integer');
  }
}

Type guard

const isValidTimes = (t: unknown): t is number =>
  typeof t === 'number' && Number.isInteger(t) && t > 0;

Try / catch

try {
  return await asyncRetry(fn, options);
} catch (e) {
  if (e instanceof Error && e.message.includes('must be a positive integer')) {
    return await asyncRetry(fn, { ...options, times: 3 });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling asyncRetry(fn, { times: 0 }), { times: -1 }, or { times: undefined as any } — e.g. computing times from config/environment where a default is missing or parsed incorrectly (parseInt of a bad string yielding NaN).

Common situations: RETRY_TIMES env var unset and parsed to NaN; config default of 0 intended to mean 'disabled' rather than 'no attempts'; YAML/JSON config typo (times: null).

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/b5fadc9c7f582061. Report an issue: GitHub.