emberjs/ember.js · error · Error

Custom component managers must have a `capabilities` propert

Error message

Custom component managers must have a `capabilities` property that is the result of calling the `capabilities('3.13')` (imported via `import { capabilities } from '@ember/component';`). Received: `${JSON.stringify(delegate.capabilities)}` for: `${delegate}`

What it means

When a component definition is first used, `getDelegateFor` instantiates the custom component manager and verifies its `capabilities` property was produced by the real `capabilities('3.13')` function (tracked via an internal FROM_CAPABILITIES set). A manager whose capabilities object was hand-built, faked, or produced by a different copy of the library fails this check and throws, including a JSON dump of what was received.

Source

Thrown at packages/@glimmer/manager/lib/public/component.ts:129

  O extends Owner,
  ComponentInstance,
> implements InternalComponentManager<CustomComponentState<ComponentInstance>> {
  private componentManagerDelegates = new WeakMap<O, ComponentManager<ComponentInstance>>();

  constructor(private factory: ManagerFactory<O, ComponentManager<ComponentInstance>>) {}

  private getDelegateFor(owner: O) {
    let { componentManagerDelegates } = this;
    let delegate = componentManagerDelegates.get(owner);

    if (delegate === undefined) {
      let { factory } = this;
      delegate = factory(owner);

      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
      if (DEBUG && !FROM_CAPABILITIES!.has(delegate.capabilities)) {
        // TODO: This error message should make sense in both Ember and Glimmer https://github.com/glimmerjs/glimmer-vm/issues/1200
        throw new Error(
          `Custom component managers must have a \`capabilities\` property that is the result of calling the \`capabilities('3.13')\` (imported via \`import { capabilities } from '@ember/component';\`). Received: \`${JSON.stringify(
            delegate.capabilities
            // eslint-disable-next-line @typescript-eslint/no-base-to-string
          )}\` for: \`${delegate}\``
        );
      }

      componentManagerDelegates.set(owner, delegate);
    }

    return delegate;
  }

  create(
    owner: O,
    definition: ComponentDefinitionState,
    vmArgs: VMArguments
  ): CustomComponentState<ComponentInstance> {

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Set `static capabilities = capabilities('3.13')` on the manager class using the real import from @glimmer/manager (or Ember's @ember/component re-export)
  2. Never hand-construct a capabilities-shaped object literal
  3. Deduplicate @glimmer/manager so the manager and the capabilities producer share one module instance
  4. Inspect the JSON in the error to confirm whether capabilities is undefined (missing property) or a fake object

Example fix

// before
class MyManager {
  capabilities = { asyncLifeCycleCallbacks: true, createInstance: true };
}
// after
import { capabilities } from '@ember/component'; // or @glimmer/manager
class MyManager {
  capabilities = capabilities('3.13', { asyncLifeCycleCallbacks: true, createInstance: true });
}
Defensive patterns

Strategy: validation

Validate before calling

import { capabilities } from '@ember/component';
class MyManager {
  static create(attrs) { return new this(attrs); }
  capabilities = capabilities('3.13', { asyncLifecycleCallbacks: true, createInstance: true, updateHook: true });
}
// verify before use:
console.assert(typeof MyManager.prototype.capabilities === 'object');

Type guard

function hasRealCapabilities(m: unknown): m is { capabilities: ComponentCapabilities } { return m !== null && typeof m === 'object' && m.capabilities instanceof Object && Object.isFrozen(m.capabilities); }

Try / catch

try { owner.factoryFor('component:x').create(); } catch (e) { if (/must have a `capabilities` property/.test(e.message)) { console.error('Manager capabilities not built by capabilities(); check for duplicate @glimmer/manager copies'); } throw e; }

Prevention

When it happens

Trigger: A component manager class whose `capabilities` is a plain object literal `{ asyncLifeCycleCallbacks: true, ... }` instead of the result of `capabilities('3.13')`; or `capabilities` imported from a duplicate/mismatched copy of @glimmer/manager so the token check fails.

Common situations: Mocking capabilities in tests with plain objects; duplicated @glimmer/manager versions in node_modules; managers written against old Glimmer versions before capabilities tokens existed.

Related errors


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