BabylonJS/Babylon.js · error

Unsupported stencil OpFail mode: ${opFail}.

Error message

Unsupported stencil OpFail mode: ${opFail}.

What it means

getNativeStencilOpFail translates a Babylon.js stencil fail-op constant (Constants.KEEP, ZERO, REPLACE, INCR, DECR, INVERT, INCR_WRAP, DECR_WRAP) into the corresponding Babylon Native engine enum. If the opFail value passed to applyStencil is not one of these known constants, the switch hits default and throws. It is a guard against passing invalid or unsupported stencil state to the native backend.

Source

Thrown at packages/dev/core/src/Engines/Native/nativeHelpers.ts:253

    switch (opFail) {
        case Constants.KEEP:
            return _native.Engine.STENCIL_OP_FAIL_S_KEEP;
        case Constants.ZERO:
            return _native.Engine.STENCIL_OP_FAIL_S_ZERO;
        case Constants.REPLACE:
            return _native.Engine.STENCIL_OP_FAIL_S_REPLACE;
        case Constants.INCR:
            return _native.Engine.STENCIL_OP_FAIL_S_INCR;
        case Constants.DECR:
            return _native.Engine.STENCIL_OP_FAIL_S_DECR;
        case Constants.INVERT:
            return _native.Engine.STENCIL_OP_FAIL_S_INVERT;
        case Constants.INCR_WRAP:
            return _native.Engine.STENCIL_OP_FAIL_S_INCRSAT;
        case Constants.DECR_WRAP:
            return _native.Engine.STENCIL_OP_FAIL_S_DECRSAT;
        default:
            throw new Error(`Unsupported stencil OpFail mode: ${opFail}.`);
    }
}

export function getNativeStencilDepthFail(depthFail: number): number {
    switch (depthFail) {
        case Constants.KEEP:
            return _native.Engine.STENCIL_OP_FAIL_Z_KEEP;
        case Constants.ZERO:
            return _native.Engine.STENCIL_OP_FAIL_Z_ZERO;
        case Constants.REPLACE:
            return _native.Engine.STENCIL_OP_FAIL_Z_REPLACE;
        case Constants.INCR:
            return _native.Engine.STENCIL_OP_FAIL_Z_INCR;
        case Constants.DECR:
            return _native.Engine.STENCIL_OP_FAIL_Z_DECR;
        case Constants.INVERT:
            return _native.Engine.STENCIL_OP_FAIL_Z_INVERT;
        case Constants.INCR_WRAP:

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Pass opFail from Constants (e.g. Constants.KEEP, Constants.INCR, Constants.DECR_WRAP) instead of a raw number.
  2. Log/validate opFail before calling applyStencil to confirm it is a defined Constants stencil op.
  3. If a needed op is genuinely missing, check Babylon.js version and update, or open an issue to add the native mapping.

Example fix

// before
engine.applyStencil(true, 0x150, 0x150, 0x150); // raw GL enums
// after
engine.applyStencil(true, Constants.INCR_SAT, Constants.INCR_SAT, Constants.INCR_SAT);
Defensive patterns

Strategy: validation

Validate before calling

const OPS = [BABYLON.Constants.KEEP, BABYLON.Constants.ZERO, BABYLON.Constants.REPLACE, BABYLON.Constants.INCR, BABYLON.Constants.DECR, BABYLON.Constants.INVERT, BABYLON.Constants.INCR_WRAP, BABYLON.Constants.DECR_WRAP];
if (!OPS.includes(opFail)) throw new RangeError(`opFail must be a Constants stencil op, got ${opFail}`);
engine.applyStencil(face, opFail, depthFail, opPass);

Type guard

function isStencilOp(v: unknown): v is number {
  const C = BABYLON.Constants;
  return typeof v === 'number' && [C.KEEP, C.ZERO, C.REPLACE, C.INCR, C.DECR, C.INVERT, C.INCR_WRAP, C.DECR_WRAP].includes(v);
}

Try / catch

try {
  engine.applyStencil(face, opFail, depthFail, opPass);
} catch (e) {
  if (String(e.message).startsWith('Unsupported stencil OpFail')) {
    console.warn('Bad opFail, falling back to KEEP', opFail);
    engine.applyStencil(face, BABYLON.Constants.KEEP, depthFail, opPass);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling engine.applyStencil(faceMode, opFail, ...) with an opFail value that is not one of the Constants stencil ops, e.g. a raw integer that doesn't match any case, undefined/null, or a value from a different API.

Common situations: Hand-rolling stencil state with magic numbers instead of Constants enums; porting WebGL code to Babylon Native where an op constant is out of range; a typo or stale constant from another Babylon version.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/8e8f80c26262454c. Report an issue: GitHub.