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 (added in front of any identifiersPrefix you set, so checkout_ becomes vm_checkout_...). 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 let vmd declarations land in the same scope and the parser rejects the second one.

Each VM-obfuscated file declares one unsuffixed top-level runtime name, and that is enough to collide. renameGlobals: true makes it much worse: every top-level declaration becomes a short vm* name, and the runtime's own names lose their unique suffix.

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 to each file within one multi-file batch (paid plans); files from separate batches can still collide. If you use a bundler plugin, verify its behavior instead of assuming it does the same. If you call the javascript-obfuscator package or the API yourself, you have to opt in to a prefix.

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.