meteor/meteor · error · Error

options.on must be an array

Error message

options.on must be an array

What it means

Thrown by Meteor.deferrable when its options.on value is present but not an array. deferrable wraps a function so it runs in the background only in the listed environments (development/production/test); the on field must be an array of those string names. A non-array value is rejected before any environment comparison so the contract is explicit.

Source

Thrown at packages/meteor/timers.js:100

 */
Meteor.defer = function (f) {
  Meteor._setImmediate(bindAndCatch("defer callback", f));
};

/**
 * @memberOf Meteor
 * @summary Wrap a function so that it only runs in background in specified environments..
 * @locus Anywhere
 * @param {Function} func The function to run
 * @param {Object} options The options object
 * @param {Array<String>} options.on Condition to determine whether to defer the function, you can pass an array of environments ['development', 'production', 'test']
 */
Meteor.deferrable = function (f, options) {
  var on = (options && options.on) || [];

  // throw if on is not an array
  if (!Array.isArray(on)) {
    throw new Error("options.on must be an array");
  }

  var env = Meteor.isDevelopment
    ? "development"
    : Meteor.isProduction
    ? "production"
    : "test";

  if (on.includes(env)) {
    return Meteor.defer(f);
  }

  return f();
};

/**
 * @memberOf Meteor
 * @summary Wrap a function to run in the background in development (similar to Meteor.isDevelopment ? Meteor.defer(fn) : Meteor.startup(fn)).

View on GitHub (pinned to 5076d2f818)

Solutions

  1. Pass an array literal: Meteor.deferrable(fn, { on: ['production'] }).
  2. If the env list comes from config, normalize it first: `const on = Array.isArray(cfg.envs) ? cfg.envs : [cfg.envs]`.
  3. Prefer the convenience wrappers Meteor.deferDev and Meteor.deferProd when you only need a fixed environment.
  4. Omit options.on entirely if you never want deferred execution (the function then runs synchronously).

Example fix

// before
Meteor.deferrable(flushCache, { on: 'production' });
// after
Meteor.deferrable(flushCache, { on: ['production'] });
Defensive patterns

Strategy: type-guard

Validate before calling

function deferrableSafe(fn, options) {
  const opts = options || {};
  if (opts.on !== undefined && !Array.isArray(opts.on)) {
    opts.on = Array.isArray(opts.on) ? opts.on : [opts.on];
  }
  return Meteor.deferrable(fn, opts);
}

Type guard

function isValidOn(on) {
  return on === undefined || Array.isArray(on);
}

Prevention

When it happens

Trigger: Calling Meteor.deferrable(fn, { on: 'production' }) with a string instead of an array; passing { on: true } or { on: { env: 'production' } }; passing options.on as undefined is allowed (defaults to []), but any other non-array type throws.

Common situations: Migrating from Meteor.deferDev/Meteor.deferProd (which build the array internally) to Meteor.deferrable and forgetting the array literal; reading the env list from a config file as a string; copying example code that dropped the brackets.

Related errors


AI-assisted analysis of meteor/meteor@5076d2f818 (2026-08-13). Data as JSON: /api/errors/7325e82c78ad8ccb. Report an issue: GitHub.