Troubleshooting
Almost every common failure has a designated fix that does not involve editing generated artifacts. Read the error literally, work out which layer it belongs to - build, link, binding, runtime, hosting - and apply the least invasive fix that layer offers.
Build errors
ENOENT: no such file or directory … /dist/…
A dependency has not been built yet. Build the whole graph (npm install && npm run build) or build that dependency first - a filtered single-package build skips its dependencies.
undefined symbol: <name>
- A missing dependency. The package providing the symbol is not in
dependencies- add it to bothcppjs.config.jsandpackage.json. - A symbol clash. Two libraries export the same name (classically
iconv). Rename one set with areplaceListentry incppjs.build.js, or drop the duplicate archive withtargetSpecs[].specs.ignoreLibName.
cannot find -l<libname>
The dependency built, but produced a different .a name than expected. Check export.libName in that package and confirm the file exists under its dist/.../lib/.
CPU intrinsics that will not compile
Upstream code using __asm__, CPL_CPUID or <immintrin.h> has no wasm equivalent. Gate it behind #ifdef __wasm__ through a replaceList entry:
shared-memory is disallowed … not compiled with atomics
A multithreaded link pulled in a Rust archive built without the atomics features - almost always a stale cargo-type prebuilt. Rebuild that package's wasm output (after rustup toolchain install nightly --component rust-src), or update to a version whose mt prebuilt was produced that way.
RuntimeError: index out of bounds during the build
Emscripten itself ran out of memory while linking. Raise it with targetSpecs[].specs.emccFlags: ['-sINITIAL_MEMORY=512MB'].
Binding errors
A function silently returns null or undefined
The C++ broke one of the binding rules. In order of likelihood:
- Returning
unique_ptrinstead ofshared_ptr. - Returning a raw pointer - wrap it, or return by value.
- Multiple inheritance in the bound class.
- A template with no explicit instantiation.
- The definition lives only in the
.cpp; the public surface has to be in the header.
Tried to call … but the function is not exposed
The function is not on the public binding surface: an anonymous namespace, a file-scope static, or a declaration and definition that disagree on static/inline.
Runtime errors
| Message | Cause | Fix |
|---|---|---|
crossOriginIsolated is false, SharedArrayBuffer is not defined | multithreaded build, production host not sending the isolation headers | add COOP/COEP - see Threading |
OPFS is only available inside a Worker scope | mounting /opfs/... from the main thread | initNative({ useWorker: true }) |
OPFS is disabled. Enable fs.opfs in config | fs: { opfs: false } was set explicitly | remove it, or write under /memfs/... |
RuntimeError: out of memory | the wasm heap hit its ceiling | raise -sINITIAL_MEMORY / -sMAXIMUM_MEMORY, or stream the input instead of holding it all |
m.someFunc is undefined | a binding rule violation, or a call made before await initNative(...) resolved | check the rules first, then the ordering |
instance.method is not a function | a worker runtime: construction returned a promise | const c = await new X(...), and set dts: 'promise' |
Memory growth is enabled by default, so an out-of-memory error usually means the design holds too much at once rather than that a limit is set too low.
Calls hang forever with useWorker: true
Either the worker never spawned - check the Network tab for a request to the worker script - or the C++ inside it is in an infinite loop, which the Sources tab on the worker context will show.
Cross-cutting symptoms
- `wasm streaming compile failed`. Either the host serves
.wasmasapplication/octet-streaminstead ofapplication/wasm, ormtandstartifacts got mixed - clean and rebuild after switching runtime. - The build succeeds but produces nothing. Look for
Skipping target: ...in the log: afunctions.isEnabledoverride or atarget.platformfilter is excluding everything. - Hot reload ignores a `.cpp` change. Restart the dev server, then verify
paths.nativeresolves where you think it does - the plugins add exactly those files to the watcher.
When nothing above matches
- Run the doctor script - most "weird" build failures are a missing toolchain (Node, Docker, Android SDK/NDK, Xcode).
- Set
LOG_LEVEL: 'DEBUG'in~/.cppjs.jsonfor verbose tracing; it usually names the failing step. - Reduce to the smallest reproducer: a fresh project with only the failing dependency.
- Search the error text in the source - error messages are unique enough to find where they are thrown.
- File an issue when the error comes from the toolchain itself rather than from your configuration or the upstream library.