emberjs/ember.js · error · Error

b.concat requires at least one part

Error message

b.concat requires at least one part

What it means

The AST builder helper `b.concat(parts)` must receive a non-empty array of TextNode/MustacheStatement parts. buildConcat uses isPresentArray to reject empty (or null-length) arrays because an AST ConcatStatement with zero parts is meaningless and would serialize to nothing.

Source

Thrown at packages/@glimmer/syntax/lib/v1/public-builders.ts:139

  return b.comment({
    value: value,
    loc: buildLoc(loc || null),
  });
}

function buildMustacheComment(value: string, loc?: SourceLocation): ASTv1.MustacheCommentStatement {
  return b.mustacheComment({
    value: value,
    loc: buildLoc(loc || null),
  });
}

function buildConcat(
  parts: (ASTv1.TextNode | ASTv1.MustacheStatement)[],
  loc?: SourceLocation
): ASTv1.ConcatStatement {
  if (!isPresentArray(parts)) {
    throw new Error(`b.concat requires at least one part`);
  }

  return b.concat({
    parts,
    loc: buildLoc(loc || null),
  });
}

// Nodes

export type ElementParts =
  | ['attrs', ...AttrSexp[]]
  | ['modifiers', ...ModifierSexp[]]
  | ['body', ...ASTv1.Statement[]]
  | ['comments', ...ElementComment[]]
  | ['as', ...string[]]
  | ['loc', SourceLocation];

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Guard before calling: only call b.concat when parts.length > 0.
  2. When parts is empty, return a TextNode (e.g. b.text('')) instead of a concat.
  3. Fix the upstream collection logic so at least one part is always produced.

Example fix

// before
return b.concat(parts);

// after
if (parts.length === 0) {
  return b.text('');
}
return b.concat(parts);
Defensive patterns

Strategy: validation

Validate before calling

function safeConcat(b, parts, loc) {
  if (!Array.isArray(parts) || parts.length === 0) return b.text('');
  return b.concat(parts, loc);
}

Type guard

function isNonEmptyParts(parts) {
  return Array.isArray(parts) && parts.length > 0 && parts.every((p) => p && (p.type === 'TextNode' || p.type === 'MustacheStatement'));
}

Try / catch

try {
  return b.concat(parts);
} catch (e) {
  if (e.message.includes('b.concat requires at least one part')) {
    return b.text('');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `b.concat([])` (or passing an array that filters down to empty at runtime) when programmatically building Glimmer templates — e.g. AST plugins/codemods that accumulate parts conditionally and end up with none.

Common situations: Writing an Ember template AST transform/codemod that builds `{{...}}` interpolations dynamically; collecting parts from user input that turned out empty.

Related errors


AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01). Data as JSON: /api/errors/3619772c5b5f2eec. Report an issue: GitHub.