phaserjs/phaser · error · Error

cannot override final property '${k}', set Class.ignoreFinal

Error message

cannot override final property '${k}', set Class.ignoreFinals = true to skip

What it means

Thrown by Phaser.Class's internal extend() helper when a definition tries to redefine a prototype property that already exists on the parent (or the ctor) with configurable:false. The framework refuses to silently redefine a 'final' property and points the developer at the opt-out. This is the ES5 class-system equivalent of a sealed/final member. The error message names the offending property (k) so you know which member collided.

Source

Thrown at src/utils/Class.js:109

        {
            //  If Extends is used, we will check its prototype to see if the final variable exists.

            var parent = extend || ctor;

            if (hasNonConfigurable(parent.prototype, k))
            {
                //  Just skip the final property
                if (Class.ignoreFinals)
                {
                    continue;
                }

                //  We cannot re-define a property that is configurable=false.
                //  So we will consider them final and throw an error. This is by
                //  default so it is clear to the developer what is happening.
                //  You can set ignoreFinals to true if you need to extend a class
                //  which has configurable=false; it will simply not re-define final properties.
                throw new Error('cannot override final property \'' + k + '\', set Class.ignoreFinals = true to skip');
            }

            Object.defineProperty(ctor.prototype, k, def);
        }
        else
        {
            ctor.prototype[k] = definition[k];
        }
    }
}

/**
 * Applies the given `mixins` to the prototype of `myClass`.
 *
 * @function mixin
 * @ignore
 * @param {Object} myClass The constructor object to mix into.
 * @param {Object|Array<Object>} mixins The mixins to apply to the constructor.

View on GitHub (pinned to 41be1e462b)

Solutions

  1. If the override is intentional and safe, set `Phaser.Class.ignoreFinals = true` globally before defining the class (this skips redefinition of final properties, so the child keeps the parent's version).
  2. Rename the colliding property in your definition so it does not shadow the final one.
  3. If you need new behavior, override the method that USES the property rather than the property itself, or add a differently-named property and patch the consuming methods.
  4. Reset ignoreFinals to false afterward if you only want the override for one class definition.

Example fix

// before
var MyImage = new Phaser.Class({
  Extends: Phaser.GameObjects.Image,
  // 'texture' is non-configurable on the parent
  texture: 'player'
});

// after (opt-out, parent's property kept)
Phaser.Class.ignoreFinals = true;
var MyImage = new Phaser.Class({ Extends: Phaser.GameObjects.Image /* no texture override */ });
Phaser.Class.ignoreFinals = false;
Defensive patterns

Strategy: validation

Validate before calling

function canOverrideFinal(parentCtor, key) {
  var desc = Object.getOwnPropertyDescriptor(parentCtor.prototype, key);
  return !desc || desc.configurable !== false;
}

function safeClass(definition) {
  var parent = definition.Extends;
  if (parent) {
    for (var k in definition) {
      if (k === 'Extends' || k === 'Mixins' || k === 'initialize') continue;
      if (!canOverrideFinal(parent, k)) {
        Phaser.Class.ignoreFinals = true;
        break;
      }
    }
  }
  var cls = new Phaser.Class(definition);
  Phaser.Class.ignoreFinals = false;
  return cls;
}

Type guard

function isFinalProperty(parentCtor, key) {
  var desc = Object.getOwnPropertyDescriptor(parentCtor.prototype, key);
  return !!desc && desc.configurable === false;
}

Try / catch

var prev = Phaser.Class.ignoreFinals;
try {
  Phaser.Class.ignoreFinals = true;
  MyClass = new Phaser.Class(definition);
} finally {
  Phaser.Class.ignoreFinals = prev;
}

Prevention

When it happens

Trigger: Calling `new Phaser.Class({ Extends: SomePhaserClass, someFinalProp: ... })` where SomePhaserClass.prototype.someFinalProp was defined via Object.defineProperty with configurable:false. Also when a Mixin introduces a property that clashes with a non-configurable one on the target. Common with internal Phaser classes that define properties as accessors or with explicit descriptors.

Common situations: Extending an internal Phaser GameObject or system class and overriding a property that Phaser declared as final. Mixing two Phaser classes whose prototypes both define the same non-configurable key. Upgrading Phaser versions where a previously-overridable property became non-configurable. Using a minifier/transpiler that emits Object.defineProperty with configurable:false for class fields.

Related errors


AI-assisted analysis of phaserjs/phaser@41be1e462b (2026-08-13). Data as JSON: /api/errors/d2fbed59bafca6d6. Report an issue: GitHub.