trekhleb/javascript-algorithms · error · Error

Items must not be empty

Error message

Items must not be empty

What it means

The second guard inside weightedRandom(): after item/weight lengths are proven equal, an empty items array is rejected because there is nothing to sample. Because the size check runs first, weightedRandom([], []) produces this message, not the size error. The function must return an {item, index} pair, which is impossible with zero candidates, so it fails fast.

Source

Thrown at src/algorithms/statistics/weighted-random/weightedRandom.js:22

 *
 * For example:
 * - items = ['banana', 'orange', 'apple']
 * - weights = [0, 0.2, 0.8]
 * - weightedRandom(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
 * 'orange' and it will never return 'banana' (because probability of picking the banana is 0%)
 *
 * @param {any[]} items
 * @param {number[]} weights
 * @returns {{item: any, index: number}}
 */
/* eslint-disable consistent-return */
export default function weightedRandom(items, weights) {
  if (items.length !== weights.length) {
    throw new Error('Items and weights must be of the same size');
  }

  if (!items.length) {
    throw new Error('Items must not be empty');
  }

  // Preparing the cumulative weights array.
  // For example:
  // - weights = [1, 4, 3]
  // - cumulativeWeights = [1, 5, 8]
  const cumulativeWeights = [];
  for (let i = 0; i < weights.length; i += 1) {
    cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
  }

  // Getting the random number in a range of [0...sum(weights)]
  // For example:
  // - weights = [1, 4, 3]
  // - maxCumulativeWeight = 8
  // - range for the random number is [0...8]
  const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
  const randomNumber = maxCumulativeWeight * Math.random();

View on GitHub (pinned to 85293e3e2b)

Solutions

  1. Check the source of the list - it is empty at call time; log its length and the filter that produced it
  2. Guard the call site: if (items.length === 0) return a sensible default instead of sampling
  3. Fix the loader/config so the list is populated before the first draw

Example fix

// before
import weightedRandom from './src/algorithms/statistics/weighted-random/weightedRandom';
const pick = weightedRandom(prizes, prizeWeights);
// prizes and prizeWeights both [] -> throws

// after
if (prizes.length === 0) {
  return null; // no prizes configured
}
const pick = weightedRandom(prizes, prizeWeights);
Defensive patterns

Strategy: validation

Validate before calling

if (!Array.isArray(items) || items.length === 0) {
  return null; // nothing to sample
}
weightedRandom(items, weights);

Type guard

const isNonEmptyArray = (a) => Array.isArray(a) && a.length > 0;

Try / catch

try {
  pick = weightedRandom(items, weights);
} catch (e) {
  if (e.message === 'Items must not be empty') {
    pick = null; // empty pool
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: weightedRandom([], []); a prize/loot/name table that loaded empty because a filter removed everything, the config list is empty, or the database query returned no rows before the first draw.

Common situations: Empty configuration at first startup, over-matching filters, feature flags disabling all options, and placeholder empty arrays left in test fixtures.

Related errors


AI-assisted analysis of trekhleb/javascript-algorithms@85293e3e2b (2026-08-24). Data as JSON: /api/errors/a6ba06795c2288f5. Report an issue: GitHub.