{"record":{"id":"7325e82c78ad8ccb","repo":"meteor/meteor","slug":"options-on-must-be-an-array","errorCode":null,"errorMessage":"options.on must be an array","messagePattern":"options\\.on must be an array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/meteor/timers.js","lineNumber":100,"sourceCode":" */\nMeteor.defer = function (f) {\n  Meteor._setImmediate(bindAndCatch(\"defer callback\", f));\n};\n\n/**\n * @memberOf Meteor\n * @summary Wrap a function so that it only runs in background in specified environments..\n * @locus Anywhere\n * @param {Function} func The function to run\n * @param {Object} options The options object\n * @param {Array<String>} options.on Condition to determine whether to defer the function, you can pass an array of environments ['development', 'production', 'test']\n */\nMeteor.deferrable = function (f, options) {\n  var on = (options && options.on) || [];\n\n  // throw if on is not an array\n  if (!Array.isArray(on)) {\n    throw new Error(\"options.on must be an array\");\n  }\n\n  var env = Meteor.isDevelopment\n    ? \"development\"\n    : Meteor.isProduction\n    ? \"production\"\n    : \"test\";\n\n  if (on.includes(env)) {\n    return Meteor.defer(f);\n  }\n\n  return f();\n};\n\n/**\n * @memberOf Meteor\n * @summary Wrap a function to run in the background in development (similar to Meteor.isDevelopment ? Meteor.defer(fn) : Meteor.startup(fn)).","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/meteor/meteor/blob/5076d2f818d3cac01d0cf8312fa5b2332076a4fb/packages/meteor/timers.js#L82-L118","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass an array literal: Meteor.deferrable(fn, { on: ['production'] }).","If the env list comes from config, normalize it first: `const on = Array.isArray(cfg.envs) ? cfg.envs : [cfg.envs]`.","Prefer the convenience wrappers Meteor.deferDev and Meteor.deferProd when you only need a fixed environment.","Omit options.on entirely if you never want deferred execution (the function then runs synchronously)."],"exampleFix":"// before\nMeteor.deferrable(flushCache, { on: 'production' });\n// after\nMeteor.deferrable(flushCache, { on: ['production'] });","handlingStrategy":"type-guard","validationCode":"function deferrableSafe(fn, options) {\n  const opts = options || {};\n  if (opts.on !== undefined && !Array.isArray(opts.on)) {\n    opts.on = Array.isArray(opts.on) ? opts.on : [opts.on];\n  }\n  return Meteor.deferrable(fn, opts);\n}","typeGuard":"function isValidOn(on) {\n  return on === undefined || Array.isArray(on);\n}","tryCatchPattern":null,"preventionTips":["Always pass options.on as an array literal.","Prefer Meteor.deferDev / Meteor.deferProd for fixed environments.","Normalize config-derived env lists to arrays before calling deferrable."],"tags":["meteor","timers","deferrable","validation","api-misuse"],"backgroundTag":null,"analyzedSha":"5076d2f818d3cac01d0cf8312fa5b2332076a4fb","analyzedAt":"2026-08-13T03:27:40.142Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}