Documentation
/

Troubleshooting

/

Avoiding Identifier Collisions

Avoiding Identifier Collisions Across Obfuscated Files

When several VM-obfuscated files end up in the same bundle, they can declare the same top-level identifier and crash the page at parse time. Here is why it happens and how to fix it.

The symptom

Your build runs fine, but the browser throws at parse time, before the app starts:

Text

To see how many chunks declare the name from the error, count the files that contain a declaration of it:

Text

-l lists each matching file once and -w matches whole words only, so vmdX is not counted. var is left out because repeating a var declaration is not an error on its own. A result above 1 means the name is declared in more than one chunk. Replace vmd with the name from your error message.

Identifier collisions only matter when the declarations share a scope. The same name repeated in separate modules or closures is not proof of a collision; the parse error above is.

Why this happens

VM presets use the mangled-shuffled identifier-names generator. It walks a small alphabet in a shuffled order, and under VM obfuscation every renamed global gets a vm prefix (the default identifiersPrefix). The shuffled order is cached per process, so files obfuscated in the same process, or with the same fixed seed, draw from the same sequence - the first global in each file shares a name, the second shares another, and so on.

Each file is obfuscated independently and the generator restarts from the beginning of its sequence for every one. When two files end up in the same scope, the names collide. Two top-level const vmd = … declarations land in the same scope and the parser rejects the second one.

Options that introduce more top-level identifiers increase the odds that two files reach the same generated name. vmWrapTopLevelInitializers is one of them, and every VM preset already enables it; options such as vmDynamicOpcodes or vmBytecodeEncoding add more.

Fixes

Prefer bundling first, then obfuscating the bundle once. If you do obfuscate separately and the scripts share a global scope, pick one of the following:

  • Enable randomIdentifiersPrefix (recommended)

    Each obfuscation run gets a random prefix prepended to every global identifier (under VM obfuscation it replaces the default vm prefix). Names from different files no longer share a namespace, so collisions disappear without you having to coordinate prefixes by hand.

  • Set a unique identifiersPrefix per file

    Manually pass a different prefix when obfuscating each file (e.g. identifiersPrefix: 'auth_' for one, checkout_ for another). Effective, but error-prone if you have many files - prefer the random option above.

    JavaScript

  • Switch identifierNamesGenerator to hexadecimal

    Hexadecimal names use a much larger keyspace, so two files are far less likely to produce the same identifier - but uniqueness is not guaranteed. Trade-off: identifiers are longer than mangled-shuffled output, so the bundle is slightly larger.

  • Use the multi-file batch UI, or check your bundler plugin

    The dashboard adds a distinct prefix per file when you use multi-file batch obfuscation, which is available on paid plans. If you use a bundler plugin, verify its behavior instead of assuming it does the same. If you're wiring up obfuscation manually with the npm API, you have to opt in to a prefix yourself.

To verify the fix, rebuild and reload the combined application and confirm the SyntaxError is gone. The name from the old error message should no longer be declared in more than one chunk; a prefix changes every generated name, so the grep above only tells you about that specific name, not about the new ones.