handlebars-lang/handlebars.js · error · Exception

Must pass iterator to #each

Error message

Must pass iterator to #each

What it means

Handlebars' built-in #each helper requires an options object (the compiled block containing options.fn and options.inverse). This error is thrown when #each is invoked without that second argument — i.e. the helper was called programmatically or as a plain helper without a block. In templates this normally only happens via handlebars.callHelper-style misuse or partials invoking helpers manually.

Source

Thrown at lib/handlebars/helpers/each.js:7

import { Exception } from '@handlebars/parser';
import { createFrame, isArray, isFunction, isMap, isSet } from '../utils.js';

export default function (instance) {
  instance.registerHelper('each', function (context, options) {
    if (!options) {
      throw new Exception('Must pass iterator to #each');
    }

    let fn = options.fn,
      inverse = options.inverse,
      i = 0,
      ret = '',
      data;

    if (isFunction(context)) {
      context = context.call(this);
    }

    if (options.data) {
      data = createFrame(options.data);
    }

    function execIteration(field, value, index, last) {
      if (data) {

View on GitHub (pinned to 13a7a67991)

Solutions

  1. Always pass the options object when calling the each helper programmatically: each(context, options) with options containing fn/inverse/hash.
  2. In a custom wrapper helper, forward the options argument: return instance.helpers.each.call(this, context, options).
  3. In templates, ensure #each is written as a block helper: {{#each items}}...{{/each}}, not a bare {{each}}.
  4. In tests, build a minimal options object: each(items, {fn: item => item, inverse: () => ''}).

Example fix

// before
Handlebars.helpers.each(items);
// after
Handlebars.helpers.each(items, { fn: item => item, inverse: () => '', hash: {} });
Defensive patterns

Strategy: validation

Validate before calling

function safeEach(instance, context, options) {
  if (!options || typeof options.fn !== 'function') {
    throw new TypeError('safeEach requires options with fn (block)');
  }
  return instance.helpers.each.call(this, context, options);
}

Type guard

function hasEachOptions(options) {
  return options != null && typeof options === 'object' && typeof options.fn === 'function';
}

Try / catch

try {
  return Handlebars.helpers.each(items, options);
} catch (e) {
  if (e.message.includes('Must pass iterator to #each')) {
    console.error('each invoked without block options; pass {fn, inverse, hash}');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling instance.helpers.each(context) directly with no options argument; invoking the helper via hbs.helpers.each in a unit test without passing {fn, inverse, hash}; template AST referencing {{each}} where the parser couldn't attach a block (custom parser/misuse); using helperMissing-style invocation where options got dropped.

Common situations: Unit-testing custom helpers by calling Handlebars.helpers.each directly; migrating from older Handlebars versions where helper invocation signatures differed; wrapping #each in a custom helper but forgetting to forward options.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of handlebars-lang/handlebars.js@13a7a67991 (2026-09-02). Data as JSON: /api/errors/8a27a3adb116a2b9. Report an issue: GitHub.