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
- Always pass the options object when calling the each helper programmatically: each(context, options) with options containing fn/inverse/hash.
- In a custom wrapper helper, forward the options argument: return instance.helpers.each.call(this, context, options).
- In templates, ensure #each is written as a block helper: {{#each items}}...{{/each}}, not a bare {{each}}.
- 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
- Never call helpers directly without forwarding the options argument
- In wrapper helpers, always pass options through unchanged
- Write #each as a block helper {{#each x}}...{{/each}} in templates
- In tests, construct a minimal options object with fn and inverse
- Lint templates for bare {{each}} references
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
- No environment passed to template
- Must define at least one template or directory.
- Invalid stack pop
- Missing helper: "${name}"
- #if requires exactly one argument
AI-assisted analysis of handlebars-lang/handlebars.js@13a7a67991 (2026-09-02).
Data as JSON: /api/errors/8a27a3adb116a2b9.
Report an issue: GitHub.