BabylonJS/Babylon.js · error

Unsupported stencil depthFail mode: ${depthFail}.

Error message

Unsupported stencil depthFail mode: ${depthFail}.

What it means

getNativeStencilDepthFail maps the stencil depth-fail op constant to the Babylon Native STENCIL_OP_FAIL_Z_* enum. Any depthFail value passed via applyStencil that doesn't match a known Constants stencil op falls to the default case and throws. Same guard pattern as the OpFail variant, for the depth-fail slot.

Source

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

    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:
            return _native.Engine.STENCIL_OP_FAIL_Z_INCRSAT;
        case Constants.DECR_WRAP:
            return _native.Engine.STENCIL_OP_FAIL_Z_DECRSAT;
        default:
            throw new Error(`Unsupported stencil depthFail mode: ${depthFail}.`);
    }
}

export function getNativeStencilDepthPass(opPass: number): number {
    switch (opPass) {
        case Constants.KEEP:
            return _native.Engine.STENCIL_OP_PASS_Z_KEEP;
        case Constants.ZERO:
            return _native.Engine.STENCIL_OP_PASS_Z_ZERO;
        case Constants.REPLACE:
            return _native.Engine.STENCIL_OP_PASS_Z_REPLACE;
        case Constants.INCR:
            return _native.Engine.STENCIL_OP_PASS_Z_INCR;
        case Constants.DECR:
            return _native.Engine.STENCIL_OP_PASS_Z_DECR;
        case Constants.INVERT:
            return _native.Engine.STENCIL_OP_PASS_Z_INVERT;
        case Constants.INCR_WRAP:

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Pass a valid Constants stencil op for depthFail (e.g. Constants.KEEP, Constants.DECR_WRAP).
  2. Verify argument order in applyStencil so the depth-fail op lands in the third parameter.
  3. Confirm the value is a number defined on Constants before calling.

Example fix

// before
engine.applyStencil(true, Constants.KEEP, gl.GL_INCR, Constants.KEEP);
// after
engine.applyStencil(true, Constants.KEEP, Constants.INCR, Constants.KEEP);
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(depthFail)) throw new RangeError(`depthFail must be a Constants stencil op, got ${depthFail}`);

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).includes('depthFail mode')) {
    console.warn('Bad depthFail, using KEEP', depthFail);
    engine.applyStencil(face, opFail, BABYLON.Constants.KEEP, opPass);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling engine.applyStencil(faceMode, opFail, depthFail) with a depthFail value not among Constants.KEEP/ZERO/REPLACE/INCR/DECR/INVERT/INCR_WRAP/DECR_WRAP, such as an arbitrary integer or undefined.

Common situations: Using raw WebGL GLenum values instead of Babylon Constants; passing swapped arguments (op in the depthFail position); older code using constants removed in newer versions.

Related errors


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