Diagnosing ClojureScript Advanced Build ReferenceErrors from Missing Externs
Diagnose and fix ClojureScript advanced‑build ReferenceErrors caused by missing externs, with checks, fixes, and escalation steps.
22 Jul 2025, 01:19 UTC

Recognizable Condition
After deploying a release build with :optimizations :advanced, the application throws a ReferenceError or TypeError about an undefined property or function (e.g., Cannot read property 'foo' of undefined) that never appeared in a development build using :optimizations :none or :whitespace.
Cause/Diagnostic Table
| Cause | Typical Symptom |
|---|---|
| Missing externs for a JavaScript API (DOM, console, third‑party lib) | Compiler renames the symbol; runtime tries to access the original name and fails. |
External library not declared via :foreign-libs or :npm-deps | Symbol is treated as unknown and mangled, causing missing‑global errors. |
String‑based property access ((aget obj 'prop')) that the compiler cannot infer | Renamed property name does not match the string literal. |
Dynamic require or eval‑like constructs bypassing static analysis | Generated code calls a mangled identifier that does not exist. |
Ordered Checks
- Run a dev build (
:optimizations :none) and confirm the UI works without errors. - Produce an advanced build and inspect the generated JavaScript for mangled identifiers (look for sequences like
$a$b$or short random names). - Check the compiler output for warnings such as ‘undeclared var’ or ‘externs’ messages.
- Review the build configuration (
project.clj,shadow-cljs.edn, ordeps.edn) for an:externsvector and for proper:foreign-libsor:npm-depsentries. - Temporarily change
:optimizationsto:whitespace(or:none) and rebuild; if the error disappears, the problem is advanced‑only.
Fixes Tied to Findings
- Add the appropriate externs file to the
:externsvector, e.g.:externs ['resources/externs/react.js']for React APIs. - Declare external libraries correctly:
- For a plain JS file:
:foreign-libs [{:file 'libs/jquery.js' :provides ['jquery']}] - For an npm package:
:npm-deps [{:package 'react' :version '17.0.2'}](adjust version as needed).
- For a plain JS file:
- Replace string‑based property access with keyword‑based access or
goog.object/get/goog.object/setwhich the compiler treats as stable. - Prevent renaming of dynamic symbols by adding
^:exportmetadata or usinggoog.exportSymbol/goog.exportVar. - Re‑run the advanced build and verify the error is gone.
Escalation Criteria
If the problem persists after adding externs and confirming library declarations:
- Create a minimal reproducible example and file an issue in the ClojureScript JIRA.
- Ask for library‑specific externs in the ClojureVerse forum or the Slack
#clojurescriptchannel. - As a temporary workaround, keep the affected module at
:optimizations :noneor:whitespacewhile you investigate further.
Verification
- Run the dev build (
:optimizations :none) and confirm the application works. - Produce an advanced build with
:pseudo-names true; open the output and verify that the problematic symbols retain their original names. - After applying fixes, run the advanced build again and execute the full test suite or manual scenario; the previous
ReferenceError/TypeErrorshould no longer appear.
Limitations
Adding overly broad externs (for example, an externs file that declares every possible property) defeats dead‑code elimination and increases the size of the generated JavaScript. Use externs that expose only the symbols you actually reference.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.