meteor/meteor · error · TypeError

Reduce of empty array with no initial value

Error message

Reduce of empty array with no initial value

What it means

Underscore's _.reduce / _.foldl / _.inject throw a TypeError with this message when the collection is empty and no initial value (memo) was supplied. With nothing to seed the accumulator, the fold is undefined; the error mirrors ES5 native Array.prototype.reduce behavior.

Source

Thrown at packages/deprecated/underscore/underscore.js:150

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
    var initial = arguments.length > 2;
    if (obj == null) obj = [];
    if (nativeReduce && obj.reduce === nativeReduce) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
    }
    each(obj, function(value, index, list) {
      if (!initial) {
        memo = value;
        initial = true;
      } else {
        memo = iterator.call(context, memo, value, index, list);
      }
    });
    if (!initial) throw new TypeError(reduceError);
    return memo;
  };

  // The right-associative version of reduce, also known as `foldr`.
  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
    var initial = arguments.length > 2;
    if (obj == null) obj = [];
    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
      if (context) iterator = _.bind(iterator, context);
      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
    }
    var length = obj.length;
    if (!looksLikeArray(obj)) {
      var keys = _.keys(obj);
      length = keys.length;
    }
    each(obj, function(value, index, list) {

View on GitHub (pinned to 5076d2f818)

Solutions

  1. Pass an explicit initial value as the third argument: _.reduce(arr, fn, initialValue).
  2. Guard the call: if (arr.length) _.reduce(arr, fn) else useDefault().
  3. Use native arr.reduce(fn, seed) with a seed, which behaves identically and avoids the throw.

Example fix

// before
const total = _.reduce(items, (m, x) => m + x.value, 0);
// but somewhere else:
_.reduce(items, (m, x) => m + x); // no seed, empty items throws

// after
const total = _.reduce(items, (m, x) => m + x.value, 0); // seed 0
Defensive patterns

Strategy: validation

Validate before calling

function reduceSafe(arr, fn, seed) {
  if (arguments.length < 3) {
    if (!arr || (arr.length !== undefined ? arr.length === 0 : _.keys(arr).length === 0)) {
      throw new TypeError('Reduce of empty array with no initial value');
    }
  }
  return _.reduce.apply(null, arguments);
}

Type guard

function hasInitialValue(args) {
  return args.length > 2;
}

Prevention

When it happens

Trigger: Calling _.reduce([], fn) or _.reduce(obj, fn) where obj has no enumerable values and omitting the third memo argument.

Common situations: Reducing over a query/filter result that can be empty; refactoring code that previously always had data; chaining _.reduce after _.filter that yields zero items.

Related errors


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