BabylonJS/Babylon.js · error · Error

`Symbol "${symbol}" is reserved and cannot be used`

Error message

`Symbol "${symbol}" is reserved and cannot be used`

What it means

During symbol decoration, ParseFragmentShader renames every uniform, const, define, and function name found in the shader. Before renaming, each symbol is checked against ReservedSymbols; if the shader uses a reserved word as a symbol name, decoration would corrupt the code, so this error is thrown naming the symbol.

Source

Thrown at packages/dev/smartFilters/src/utils/buildTools/shaderConverter.ts:272

    log(`Uniforms found: ${JSON.stringify(uniforms)}`);
    const consts = [...fragmentShader.matchAll(/\S*const\s+\w*\s+(\w*)\s*=.*;/g)].map((match) => match[1]);
    log(`Consts found: ${JSON.stringify(consts)}`);
    const constPropertyFriendlyNames = fragmentConstProperties.map((c) => c.friendlyName);
    log(`Const properties found: ${JSON.stringify(constPropertyFriendlyNames)}`);
    const defineNames = [...fragmentShader.matchAll(new RegExp(GetDefineRegExString, GetDefineRegExOptions))].map((match) => match[1]);
    log(`Defines found: ${JSON.stringify(defineNames)}`);
    const functionNames = [...fragmentShaderWithNoFunctionBodies.matchAll(new RegExp(GetFunctionHeaderRegExString, GetFunctionHeaderRegExOptions))].map((match) => match[1]);
    log(`Functions found: ${JSON.stringify(functionNames)}`);

    // Decorate the uniforms, consts, defines, and functions
    const symbolsToDecorate = [...uniformNames, ...consts, ...constPropertyFriendlyNames, ...defineNames, ...functionNames];
    let fragmentShaderWithRenamedSymbols = fragmentShader;
    for (const symbol of symbolsToDecorate) {
        if (!symbol) {
            continue;
        }
        if (ReservedSymbols.indexOf(symbol) !== -1) {
            throw new Error(`Symbol "${symbol}" is reserved and cannot be used`);
        }
        const regex = new RegExp(`(?<=\\W+)${symbol}(?=\\W+)`, "gs");
        fragmentShaderWithRenamedSymbols = fragmentShaderWithRenamedSymbols.replace(regex, DecorateSymbol(symbol));
    }
    log(`${symbolsToDecorate.length} symbol(s) renamed`);

    // Extract all the uniforms
    const finalUniforms = [...fragmentShaderWithRenamedSymbols.matchAll(/^\s*(uniform\s.*)/gm)].map((match) => match[1]);

    // Extract all the consts
    const finalConsts = [...fragmentShaderWithRenamedSymbols.matchAll(/^\s*(const\s.*)/gm)].map((match) => match[1]);

    // Extract all the defines
    const finalDefines = [...fragmentShaderWithRenamedSymbols.matchAll(new RegExp(GetDefineRegExString, GetDefineRegExOptions))].map((match) => match[0]);

    // Find the main input
    const mainInputs = [...fragmentShaderWithRenamedSymbols.matchAll(/\S*uniform.*\s(\w*);\s*\/\/\s*main/gm)].map((match) => match[1]);
    if (mainInputs.length > 1) {

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Rename the offending symbol to something not in ReservedSymbols (prefix it, e.g. `myMain` → `myHelperFn`).
  2. Check the ReservedSymbols list in the smartFilters package and avoid all entries when authoring shaders.
  3. If a define collides, namespace your defines (e.g. MYBLOCK_WHATEVER).

Example fix

// before
void main(...) // main
{ ... }
void main() {} // duplicate/reserved helper
// after
void main(...) // main
{ ... }
void adjustColor() { ... }
Defensive patterns

Strategy: validation

Validate before calling

const names = [
  ...shader.matchAll(/\buniform\s+\w+\s+(\w+)/g),
  ...shader.matchAll(/\bconst\s+\w+\s+(\w+)/g),
  ...shader.matchAll(/\b\w+\s+(\w+)\s*\([^)]*\)\s*\{/g),
].map(m => m[1]);
const reserved = ["main","position","output","time"]; // keep in sync with ReservedSymbols
const clash = names.filter(n => reserved.includes(n));
if (clash.length) throw new Error(`Reserved symbols used: ${clash.join(", ")}`);

Type guard

function isNotReserved(symbol: string): boolean {
  return ReservedSymbols.indexOf(symbol) === -1;
}

Try / catch

try {
  const info = ParseFragmentShader(blockName, namespace, shader);
} catch (e) {
  if (e instanceof Error && e.message.includes("is reserved and cannot be used")) {
    const sym = /Symbol "(.+?)"/.exec(e.message)?.[1];
    // rename `sym` in the shader source and retry
  } else throw e;
}

Prevention

When it happens

Trigger: ParseFragmentShader on a shader whose uniform/const/define/function is named something in ReservedSymbols (e.g. a function named 'main', or a uniform colliding with a reserved runtime name). Any of fragmentShaderInfo/result entry points.

Common situations: Naming a helper function `main` alongside the real main; using common words like `position` or `output` that the framework reserves; porting existing GLSL code that already used framework-reserved identifiers.

Related errors


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