jestjs/jest · error · Error

${propertyName} has already been spied upon

Error message

${propertyName} has already been spied upon

What it means

Thrown by `SpyRegistry._spyOnProperty` in spyRegistry.ts:195 when the property value is already a jasmine spy and `respy` is not enabled. Same double-spy protection as the method variant, applied to property accessors.

Source

Thrown at packages/jest-jasmine2/src/jasmine/spyRegistry.ts:195

      if (!descriptor.configurable) {
        throw new Error(
          getErrorMsg(`${propertyName} is not declared configurable`),
        );
      }

      if (!descriptor[accessType]) {
        throw new Error(
          getErrorMsg(
            `Property ${propertyName} does not have access type ${accessType}`,
          ),
        );
      }

      if (obj[propertyName] && isSpy(obj[propertyName])) {
        if (this.respy) {
          return obj[propertyName];
        } else {
          throw new Error(
            getErrorMsg(`${propertyName} has already been spied upon`),
          );
        }
      }

      const originalDescriptor = descriptor;
      const spiedProperty = createSpy(propertyName, descriptor[accessType]);
      let restoreStrategy;

      if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
        restoreStrategy = function () {
          Object.defineProperty(obj, propertyName, originalDescriptor);
        };
      } else {
        restoreStrategy = function () {
          delete obj[propertyName];
        };
      }

View on GitHub (pinned to f49721c78e)

Solutions

  1. Spy once — remove the duplicate `spyOnProperty` call.
  2. Enable respy via `jasmine.getEnv().allowRespy(true)` if re-spying is intentional.
  3. Ensure isolation so the prior spy is restored before the next attempt.

Example fix

// before
beforeEach(() => spyOnProperty(cfg, 'env', 'get'));
it('x', () => { spyOnProperty(cfg, 'env', 'get'); /* throws */ });
// after
beforeEach(() => spyOnProperty(cfg, 'env', 'get'));
it('x', () => { /* reuse the existing spy */ });
Defensive patterns

Strategy: validation

Validate before calling

const cur = obj[propertyName];
if (cur && cur.and && cur.calls) { /* already a spy */ }
else spyOnProperty(obj, propertyName, 'get');

Type guard

const isSpy = (v: any): boolean =>
  v != null && v.and != null && typeof v.and === 'object' && v.calls != null;

Prevention

When it happens

Trigger: Calling `spyOnProperty(obj, 'p', 'get')` twice in the same test, or once in `beforeEach` and again in the test body.

Common situations: Shared `beforeEach` plus a per-test spy; helper that spies unconditionally; module-level object whose spies are not restored between tests.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/2d98723865e0dcc4.json. Report an issue: GitHub.