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:
To see how many chunks declare the name from the error, count the files that contain a declaration of it:
-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
vmprefix). Names from different files no longer share a namespace, so collisions disappear without you having to coordinate prefixes by hand.Set a unique
identifiersPrefixper fileManually 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.Switch
identifierNamesGeneratortohexadecimalHexadecimal 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-shuffledoutput, 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-obfuscatorpackage 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.
Related options
randomIdentifiersPrefix- random prefix per build (recommended)identifiersPrefix- explicit prefix stringidentifierNamesGenerator- name shape (mangled,hexadecimal, …)
