emberjs/ember.js · error

Attempted to invoke a component that was not in scope in a s

Error message

Attempted to invoke a component that was not in scope in a strict mode template, \`<${variable}>\`. If you wanted to create an element with that name, convert it to lowercase - \`<${variable.toLowerCase()}>\`

What it means

In strict-mode templates, an uppercase-first tag name is treated as a component invocation, so it must be bound in scope (imported component, local, or `@arg`). When it is not, the compiler throws instead of silently rendering an unknown element. Lowercase names are always treated as HTML elements.

Source

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

   * 2. If the variable is `this`, return a `ThisHead`
   * 3. If the variable is in the current scope:
   *   a. If the scope is the root scope, then return a Free `LocalVarHead`
   *   b. Else, return a standard `LocalVarHead`
   * 4. If the tag name is a path and the variable is not in the current scope, Syntax Error
   * 5. If the variable is uppercase return a FreeVar(ResolveAsComponentHead)
   * 6. Otherwise, return `'ElementHead'`
   */
  private classifyTag(
    variable: string,
    tail: string[],
    loc: SourceSpan
  ): ASTv2.ExpressionNode | 'ElementHead' {
    let uppercase = isUpperCase(variable);
    let inScope = variable[0] === '@' || variable === 'this' || this.ctx.hasBinding(variable);

    if (this.ctx.strict && !inScope) {
      if (uppercase) {
        throw generateSyntaxError(
          `Attempted to invoke a component that was not in scope in a strict mode template, \`<${variable}>\`. If you wanted to create an element with that name, convert it to lowercase - \`<${variable.toLowerCase()}>\``,
          loc
        );
      }

      // In strict mode, values are always elements unless they are in scope
      return 'ElementHead';
    }

    // Since the parser handed us the HTML element name as a string, we need
    // to convert it into an ASTv1 path so it can be processed using the
    // expression normalizer.
    let isComponent = inScope || uppercase;

    let variableLoc = loc.sliceStartChars({ skipStart: 1, chars: variable.length });

    let tailLength = tail.reduce((accum, part) => accum + 1 + part.length, 0);
    let pathEnd = variableLoc.getEnd().move(tailLength);

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Import/define the component in scope: `import SomeComponent from '...';` before using `<SomeComponent />`.
  2. If you actually wanted a plain HTML element, lowercase the tag name, e.g. `<my-widget>`.
  3. Check the component name spelling/casing matches the in-scope binding.

Example fix

// before (strict mode)
<ProfileCard @user={{this.user}} />

// after
import ProfileCard from './profile-card';
<template><ProfileCard @user={{this.user}} /></template>
Defensive patterns

Strategy: validation

Validate before calling

// Uppercase tags must be imported in strict-mode templates.
function componentTagInScope(tagName, imports /* Set<string> */) {
  return !/^[A-Z]/.test(tagName) || imports.has(tagName);
}

Prevention

When it happens

Trigger: Compiling a strict-mode template with `<SomeComponent />` where `SomeComponent` was not imported/defined in the template's scope.

Common situations: Upgrading to Ember strict-mode templates or `<template>` blocks and forgetting the import; renaming a component file so the import no longer matches; copying loose-mode templates into strict mode.

Related errors


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