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
- 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).
- Rename the colliding property in your definition so it does not shadow the final one.
- 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.
- 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
- Audit parent prototypes with Object.getOwnPropertyDescriptor before overriding in Phaser.Class.
- Prefer overriding methods that consume a property rather than the property itself.
- Keep ignoreFinals scoped: set true, define the class, reset to false in a finally block.
- Avoid non-configurable properties in your own base classes if you intend them to be overridable.
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.