emberjs/ember.js · error · Error

${fnName}() can only be used on an instance of a cache creat

Error message

${fnName}() can only be used on an instance of a cache created with createCache(). Called with: ${String(value)}

What it means

getValue() and isConst() only operate on caches produced by createCache(). In DEBUG builds assertCache checks the value is an object carrying the internal FN marker symbol; anything else throws. This catches passing a raw function, plain object, or a value created by another memoization utility.

Source

Thrown at packages/@glimmer/validator/lib/tracking.ts:205

  return cache[LAST_VALUE];
}

export function isConst(cache: Cache): boolean {
  assertCache(cache, 'isConst');

  let tag = cache[TAG];

  assertTag(tag, cache);

  return isConstTag(tag);
}

function assertCache<T>(
  value: Cache<T> | InternalCache<T>,
  fnName: string
): asserts value is InternalCache<T> {
  if (DEBUG && !(typeof value === 'object' && FN in value)) {
    throw new Error(
      `${fnName}() can only be used on an instance of a cache created with createCache(). Called with: ${String(
        // eslint-disable-next-line @typescript-eslint/no-base-to-string -- @fixme
        value
      )}`
    );
  }
}

// replace this with `expect` when we can
function assertTag(tag: Tag | undefined, cache: InternalCache): asserts tag is Tag {
  if (DEBUG && tag === undefined) {
    throw new Error(
      `isConst() can only be used on a cache once getValue() has been called at least once. Called with cache function:\n\n${String(
        cache[FN]
      )}`
    );
  }
}

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Pass exactly the object returned by createCache(), not the wrapped function or its result
  2. Verify only one copy of @glimmer/validator is installed (npm ls @glimmer/validator; dedupe)
  3. Search for getValue(/isConst( calls and confirm each argument comes from createCache
  4. Re-run against a debug build to get this check rather than silent undefined behavior in production

Example fix

// before
const value = getValue(myCompute);
// after
const cache = createCache(myCompute, 'myCompute');
const value = getValue(cache);
Defensive patterns

Strategy: validation

Validate before calling

function isCache(v) { return v !== null && typeof v === 'object' && typeof v.getValue === 'function' || (v && v.constructor && String(v).includes('cache')); }
if (!isCache(maybeCache)) throw new TypeError('getValue requires a createCache() result');

Type guard

function isCache(v) { return typeof v === 'object' && v !== null; } // exact marker symbol is internal; track creation yourself: const caches = new WeakSet(); then isCache = v => caches.has(v)

Try / catch

try { value = getValue(cache); } catch (e) { if (String(e).includes('createCache()')) { cache = createCache(fn); value = getValue(cache); } else { throw e; } }

Prevention

When it happens

Trigger: Calling getValue(x) or isConst(x) where x is not the return value of createCache() — e.g. passing the original function itself, a result of the cached function, or an object from a different memoization lib.

Common situations: Mixing up the cache object with the cached value; two copies of @glimmer/validator in the bundle so the FN symbol differs (duplicate dependency after dependency resolution changes); refactoring away createCache but leaving getValue calls.

Related errors


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