emberjs/ember.js · error · Error

You must pass either the `hasValue` OR the `hasScheduledEffe

Error message

You must pass either the `hasValue` OR the `hasScheduledEffect` capability when defining a helper manager. Passing neither, or both, is not permitted.

What it means

Helper manager capabilities require exactly one of `hasValue` or `hasScheduledEffect` to be set. Passing neither (capabilities say nothing about the helper's semantics) or both (contradictory semantics) is invalid, and `helperCapabilities()` throws in DEBUG. This forces every helper manager to declare whether it returns a value eagerly or produces a scheduled effect.

Source

Thrown at packages/@glimmer/manager/lib/public/helper.ts:41

import { argsProxyFor } from '../util/args-proxy';
import { buildCapabilities, FROM_CAPABILITIES } from '../util/capabilities';

export function helperCapabilities<Version extends keyof HelperCapabilitiesVersions>(
  managerAPI: Version,
  options: Partial<HelperCapabilities> = {}
): HelperCapabilities {
  debugAssert(
    managerAPI === '3.23',
    () =>
      `Invalid helper manager compatibility specified; you specified ${managerAPI}, but only '3.23' is supported.`
  );

  if (
    DEBUG &&
    (!(options.hasValue || options.hasScheduledEffect) ||
      (options.hasValue && options.hasScheduledEffect))
  ) {
    throw new Error(
      'You must pass either the `hasValue` OR the `hasScheduledEffect` capability when defining a helper manager. Passing neither, or both, is not permitted.'
    );
  }

  if (DEBUG && options.hasScheduledEffect) {
    throw new Error(
      'The `hasScheduledEffect` capability has not yet been implemented for helper managers. Please pass `hasValue` instead'
    );
  }

  return buildCapabilities({
    hasValue: Boolean(options.hasValue),
    hasDestroyable: Boolean(options.hasDestroyable),
    hasScheduledEffect: Boolean(options.hasScheduledEffect),
  });
}

////////////

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Pass exactly one flag: `helperCapabilities('3.23', { hasValue: true })` for value helpers
  2. Use `{ hasScheduledEffect: true }` only for effect-style helpers — but see error 59, currently unimplemented, so use hasValue
  3. Remove the other flag if both were passed
  4. Verify the option key spelling matches hasValue/hasScheduledEffect

Example fix

// before
capabilities = helperCapabilities('3.23', { hasValue: true, hasScheduledEffect: true });
// after
capabilities = helperCapabilities('3.23', { hasValue: true });
Defensive patterns

Strategy: validation

Validate before calling

import { capabilities } from '@glimmer/manager';
function buildHelperCaps(opts: { hasValue?: boolean; hasScheduledEffect?: boolean }) {
  const count = Number(!!opts.hasValue) + Number(!!opts.hasScheduledEffect);
  if (count !== 1) throw new Error('pass exactly one of hasValue or hasScheduledEffect');
  return capabilities('3.23', opts);
}

Type guard

function hasExactlyOneCapability(o: { hasValue?: boolean; hasScheduledEffect?: boolean }): boolean { return Number(!!o.hasValue) + Number(!!o.hasScheduledEffect) === 1; }

Try / catch

try { caps = helperCapabilities('3.23', opts); } catch (e) { if (/hasValue` OR the `hasScheduledEffect/.test(e.message)) caps = helperCapabilities('3.23', { hasValue: true }); else throw e; }

Prevention

When it happens

Trigger: `helperCapabilities('3.23')` with no options; with `{ hasValue: true, hasScheduledEffect: true }`; or with options typed/spelled incorrectly so both flags end up truthy or neither does (e.g. passing unrelated option keys).

Common situations: First-time helper manager authors omitting capability options; combining flags 'to be safe' from example code; refactors that renamed flags leaving both stale keys.

Related errors


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