emberjs/ember.js · error

Component has both <:else> and <:inverse> block. <:inverse>

Error message

Component has both <:else> and <:inverse> block. <:inverse> is an alias for <:else>

What it means

<:else> and <:inverse> are the same block: <:inverse> is an alias for <:else> (used for the false/else branch of conditionals). Passing both would be contradictory, so the normalizer rejects the invocation when both names appear in the seen set.

Source

Thrown at packages/@glimmer/syntax/lib/v2/normalize.ts:1036

      }

      let seenNames = new Set<string>();

      for (let block of this.namedBlocks) {
        let name = block.name.chars;

        if (seenNames.has(name)) {
          throw generateSyntaxError(
            `Component had two named blocks with the same name, \`<:${name}>\`. Only one block with a given name may be passed`,
            this.loc
          );
        }

        if (
          (name === 'inverse' && seenNames.has('else')) ||
          (name === 'else' && seenNames.has('inverse'))
        ) {
          throw generateSyntaxError(
            `Component has both <:else> and <:inverse> block. <:inverse> is an alias for <:else>`,
            this.loc
          );
        }

        seenNames.add(name);
      }

      return this.namedBlocks;
    } else {
      return [
        this.block.builder.namedBlock(
          SourceSlice.synthetic('default'),
          this.block.builder.block(table, this.nonBlockChildren, this.loc),
          this.loc
        ),
      ];
    }

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Keep only <:else> (the canonical name) and delete the <:inverse> block
  2. Or keep <:inverse> and delete <:else>, matching the component's documented convention
  3. Merge the content of both blocks into the single retained block if both bodies were intended

Example fix

// before
<Toggle>
  <:inverse>Off</:inverse>
  <:else>Disabled</:else>
</Toggle>
// after
<Toggle>
  <:else>
    Off
    Disabled
  </:else>
</Toggle>
Defensive patterns

Strategy: validation

Validate before calling

function hasElseAndInverse(src) {
  return /<:else>/.test(src) && /<:inverse>/.test(src);
}

Prevention

When it happens

Trigger: Compiling an invocation that includes both blocks, e.g. <If @cond><:inverse>a</:inverse><:else>b</:else></If> — the dedup loop's alias check (name==='inverse' && seenNames.has('else') or vice versa) throws.

Common situations: Incremental migration where one author wrote <:else> and another added <:inverse> (or vice versa), or confusion about which alias spelling the component supports.

Related errors


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