Diagnosing ClojureScript Advanced Compilation Breakage: A Step‑by‑Step Guide
Learn how to spot, diagnose, and fix JavaScript interop errors that appear only when ClojureScript is compiled with :optimizations :advanced.
25 Apr 2026, 02:54 UTC

Recognizable condition
The application runs without errors when built with :optimizations :none or :optimizations :simple, but after a release build that uses :optimizations :advanced it throws runtime errors such as Property 'foo' is not a function or undefined method. The error appears only in the compiled JavaScript bundle, not in the source‑map‑enabled development build.
Diagnostic table
| Symptom | Likely cause |
|---|---|
| Missing DOM or library method | No extern declaration for the JavaScript property or function |
| Minified name mismatch | Closure Compiler name mangling removes the expected identifier |
| Works in source‑map build, fails in release | Dead‑code elimination removed a side‑effect that the interop relies on |
| Different behavior between :simple and :advanced | Export/extern boundary issue; the namespace is not preserved under advanced |
Ordered checks
- Confirm the build profile: run
clj -M:compile(or your build tool) with:optimizations :advanced. Verify that the profile name inproject.cljordeps.ednis indeed:advanced. If you are using Leiningen, check the:profilesmap. - Inspect compiled output: locate the generated JavaScript file (usually
target/advanced/main.js). Search for the property name that the error references. If the name is mangled (e.g.,ainstead offoo), the extern is missing or incorrect. - Enable readable names and source maps: add
:source-map trueand:js-print-source trueto the advanced profile. Re‑run the build. Open the source map in a debugger to map the error back to the original ClojureScript line. - Test the same namespace under :simple: change the profile temporarily to
:optimizations :simpleand rebuild. If the error disappears, the problem is optimization‑specific. - Search for js/ interop: grep the ClojureScript sources for
js/accesses that touch the failing property. Note any third‑party library objects that are used without an extern declaration.
Fixes tied to findings
- Add an externs file: create
externs.js(or.cljs) that declares the required property. Example:
// externs.js
goog.provide('myLib.foo');
Then in my_namespace.cljs add:
(ns my-namespace
(:externs
myLib.foo))
Re‑run the advanced build. The property name is now preserved, and the runtime error disappears.
(defn ^:export myLib.foo
[args]
(js/alert "called"))
This tells Closure Compiler not to rename the function, preventing name‑mangling issues.
goog.export calls in a separate externs file that mirrors the library’s public API.:target :nodejs or :target :browser matches the browsers you support. Mismatched targets can cause the compiler to drop features that are required for the interop.:optimizations :simple to avoid the extern maintenance burden.Escalation criteria
- If errors persist after adding externs and exports, create a minimal reproducible project: one namespace, one extern file, and the exact build profile. Share the repo so the community can verify the failure.
- When
:simpleworks but:advanceddoes not, do not ship the advanced build until the extern set is validated. Shipping without validation risks runtime crashes for end users. - If the problem is isolated to a specific ClojureScript or Closure Compiler version, report the versions, the exact build config, and the minimized repro. This helps the maintainers diagnose version‑specific changes in extern handling.
Verification steps
- Check the project’s dependency versions with
clj -M:dependenciesand review the ClojureScript release notes for the bundled Closure Compiler version. - Build the problematic namespace twice: once with
:optimizations :simpleand once with:optimizations :advanced. Compare the compiled JavaScript for the missing property name and the runtime error logs. - Temporarily add an externs file containing the failing property name, rebuild with
:advanced, and confirm that the method becomes callable. Remove the externs file afterward to ensure the fix is not a temporary hack. - Use source maps with readable names to trace any remaining advanced‑compilation error back to its original ClojureScript namespace and line number. Verify that the stack trace points to the correct source location.
By following this diagnostic guide you can systematically locate the root cause of advanced‑compilation breakage, apply the appropriate extern or export fix, and verify that the solution holds before releasing the application.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.