jestjs/jest · error · Error

is not declared configurable

Error message

${propertyName} is not declared configurable

What it means

Thrown by SpyRegistry._spyOnProperty when the property descriptor's configurable flag is false (spyRegistry.ts:177-181). Object.defineProperty can only redefine a property whose descriptor is configurable; a non-configurable property cannot be replaced with a spy.

Solutions

  1. Redefine the property as configurable before spying: Object.defineProperty(obj, 'p', { configurable: true, ...rest }).
  2. Spy on a wrapper module you control rather than the locked native object.
  3. Use jest.mock to replace the entire module instead of spying on a single locked property.
  4. If testing env accessors, copy needed values to a plain object and spy on that.

Example fix

// before
spyOn(globalThis, 'location', 'get'); // non-configurable
// after
jest.mock('./env', () => ({getLocation: () => '/fake'}));
Defensive patterns

Strategy: validation

Validate before calling

const d = Object.getOwnPropertyDescriptor(obj, propertyName);
if (d && !d.configurable) {
  throw new Error(`${String(propertyName)} is non-configurable; mock the module instead`);
}
spyOn(obj, propertyName, 'get');

Type guard

const isConfigurable = (o: object, k: PropertyKey): boolean => {
  const d = Object.getOwnPropertyDescriptor(o, k);
  return !!d && !!d.configurable;
};

Prevention

When it happens

Trigger: spyOn on a property defined via Object.defineProperty(obj, 'p', { configurable: false }), built-in prototype properties like Function.prototype.arguments, frozen object accessor properties, or sealed objects.

Common situations: Spying on built-ins (window.location, process.env accessors, native prototype members); library authors who deliberately lock properties; Object.seal/Object.freeze on the target; spying on class private fields compiled to non-configurable descriptors.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/47cddda507bbe05e. Report an issue: GitHub.

Appendix: source

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

      }

      if (!propertyName) {
        throw new Error(getErrorMsg('No property name supplied'));
      }

      let descriptor: PropertyDescriptor | undefined;
      try {
        descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
      } catch {
        // IE 8 doesn't support `definePropery` on non-DOM nodes
      }

      if (!descriptor) {
        throw new Error(getErrorMsg(`${propertyName} property does not exist`));
      }

      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`),

View on GitHub (pinned to 8e6d128e4a)