caolan/async · error · Error

Invalid arguments for async.retry

Error message

Invalid arguments for async.retry

What it means

async.retry() throws 'Invalid arguments for async.retry' when the task argument passed as the last positional argument is not a function. The library expects retry(task, [opts], [callback]) where task is the function to re-attempt; anything else (undefined, an object, a string) cannot be wrapped by wrapAsync and is rejected eagerly. This is an argument-validation error designed to fail fast instead of crashing later inside the retry loop.

Source

Thrown at lib/retry.js:113

const DEFAULT_TIMES = 5;
const DEFAULT_INTERVAL = 0;

export default function retry(opts, task, callback) {
    var options = {
        times: DEFAULT_TIMES,
        intervalFunc: constant(DEFAULT_INTERVAL)
    };

    if (arguments.length < 3 && typeof opts === 'function') {
        callback = task || promiseCallback();
        task = opts;
    } else {
        parseTimes(options, opts);
        callback = callback || promiseCallback();
    }

    if (typeof task !== 'function') {
        throw new Error("Invalid arguments for async.retry");
    }

    var _task = wrapAsync(task);

    var attempt = 1;
    function retryAttempt() {
        _task((err, ...args) => {
            if (err === false) return
            if (err && attempt++ < options.times &&
                (typeof options.errorFilter != 'function' ||
                    options.errorFilter(err))) {
                setTimeout(retryAttempt, options.intervalFunc(attempt - 1));
            } else {
                callback(err, ...args);
            }
        });
    }

View on GitHub (pinned to 13dfaf13f3)

Solutions

  1. Ensure the last argument passed to async.retry is the task function to run
  2. Check the task variable is defined and typeof === 'function' before calling retry
  3. Correct the argument order: retry(timesOrOpts, task, callback) — do not omit the task
  4. If using promises, pass an async function, not a Promise instance

Example fix

// before
async.retry({times: 3}, taskConfig); // taskConfig is not a function
// after
async.retry({times: 3}, fetchWithBackoff, function(err, result) { ... });
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof task !== 'function') { throw new TypeError('retry: task must be a function, got ' + typeof task); }

Type guard

function isTask(v) { return typeof v === 'function'; }

Try / catch

try {
  async.retry(opts, task, cb);
} catch (err) {
  if (err.message.includes('Invalid arguments for async.retry')) {
    console.error('retry task argument must be a function, got:', typeof task);
  } else { throw err; }
}

Prevention

When it happens

Trigger: Calling async.retry with a non-function as the task argument: async.retry(5, opts) where the function was forgotten, async.retry(options) with only an options object and no task, passing a variable that is undefined because the task function is misnamed or not yet imported, or passing a promise instead of a callback-style function.

Common situations: Refactoring that renamed the task function leaving a stale reference (undefined at call time); passing an async/await-style function wrapper object instead of a function; copy-pasted calls like async.retry({times: 3}, config) missing the actual worker; TypeScript/JS interop where an optional task defaults to undefined.

Related errors


AI-assisted analysis of caolan/async@13dfaf13f3 (2026-08-28). Data as JSON: /api/errors/9ea5ed629795d31b. Report an issue: GitHub.