Troubleshooting
The build succeeding is not the same as the wrapper being complete. Most "bugs" are a missing TOML entry. This page is symptom → what to edit.
Missing from the wrapper, or wrong at runtime
| What you see | Look at |
|---|---|
A C++ template type is missing, or members are Any | [types] templates / template_headers |
| A struct from a header has no fields | -fstandalone-debug in [compile] flags |
| A struct became an opaque blob with a layout warning | Unmodellable member; read the warning's named type |
| A macro-based API is absent (no error) | [wrap.macros] + shim_headers |
| Macro values are wrong but everything compiled | Shim header resolved to a system copy of the same name — use a more specific path or an explicit -I |
printf-style function only takes its fixed arguments | [wrap.varargs] |
invalid type '…' in [wrap.varargs.f] | You wrote C types; varargs want Julia types (Cint, Cstring, …) |
must be a list of type lists | You wrote f = ["Cint"]; write f = [["Cint"]] |
| Memory grows on every string-returning call | [wrap.cstring_owned] |
| Unrelated symbols from tests/examples in the wrapper | exclude on the dependency |
| A TU fails to compile with a missing header | [compile] include_dirs — a directory that does not exist is passed to clang as is, so check the spelling |
| A TU fails on an identifier upstream's build defines | [compile] flags / [compile.defines] |
| A setting seems to have no effect | Typos are silent (no unknown-key warning). Also check the ignored-keys list in Edit the TOML |
using MyProject loads an old build after you edited the project | The package loads the build vendored last. use("myproject"), or register again, vendors the new one; then start a new session (Registry) |
use() keeps building an old copy of the sources | The project was registered by an earlier RepliBuild, which copied its sources into the registry. Register it again from the project |
| A wrapper warns it was generated by an older RepliBuild | Run wrap(toml) again. A fix in what build extracts also needs build(toml; clean=true) first: a build whose inputs did not change is skipped |
Details and examples for each key: Edit the TOML.
Build and environment
| What you see | What to do |
|---|---|
RepliBuild supports Linux and Windows | It does. macOS is refused because the AAPCS64 ABI classifier is unbuilt, not because it is untested. |
check_environment() marks a tool FAIL or WARN | The advice under that line is the fix. Install has the whole setup. |
LLVM Toolchain not found building C++ | No LLVM 21+ on PATH (or in LLVM_CONFIG). See Install. |
| C++ wrap fails, C works | The JLCS dialect library is not built: Install → The JLCS dialect. |
JLCS dialect library not found at: … | Same: build the dialect. The message names the folder to build in. |
check_environment(): libJLCS "built against LLVM X, but llvm-config is Y" | The system LLVM/MLIR moved to another minor version. Build the dialect again. |
Failed to initialize JIT for lib….so when a C++ wrapper loads | That library's Tier 2 functions will not work; its ccall functions still do. The error under it says why. Usually the dialect is missing or will not load, or llvm-dwarfdump is not on PATH; check_environment() checks all three. |
Windows: every C file fails on its first system #include | The Clang JLL has no headers of its own. Install MSYS2 CLANG64, or set REPLIBUILD_C_SYSROOT. |
Windows: libJLCS.dll will not load (The specified module could not be found) | It imports libLLVM-<N>.dll and libc++.dll from C:\msys64\clang64\bin. Put that directory on the Windows PATH and restart Julia. If it is already there, MSYS2 has moved to another LLVM and the DLL it was built against is gone: build the dialect again. |
Windows: The specified module could not be found opening a thunks .dll | The missing module is libJLCS.dll, not the thunks library — see the row above. |
| Windows: C++ stopped working after upgrading RepliBuild | Each version installs into a new folder, which has no dialect library yet. Build it there (Install). |
Windows: An Application Control policy has blocked this file | Smart App Control, or another App Control policy, refused to load a library RepliBuild just built. Such policies block new, unsigned binaries, and everything RepliBuild builds is one. Event 3077 in Event Viewer (Applications and Services Logs → Microsoft → Windows → CodeIntegrity → Operational) names the blocked file. RepliBuild cannot sign what it builds; whether to change the policy is your decision. |
Windows: clean fails because a file is in use | A running process still has the library loaded, usually the Julia session that included the wrapper. Close it and run clean again. |
Configuration file not found | Run discover first, or pass the path to the TOML. |
Library not found: …/julia/lib….so | Run build before wrap. |
No compilation_metadata.json next to … | build writes it beside the library, and wrap takes every type from it. Run build(toml). |
No source files to compile | [compile] source_files is empty and the scan found nothing. If the sources come from a [dependencies] entry, look further up for Failed to clone dependency. |
Failed to clone dependency … (a warning) | The git dependency was not fetched, and the build carries on without it, so it fails later on missing headers or sources. Check the URL, the tag and the network, then build again. |
Dependency HEAD disagrees with commit | The tag moved. Update commit (full 40 hex) or pin a different tag. |
Shim #include resolved outside the project | shim_headers hit a system header. More specific path, or -I. |
Wrapper generation refused (undeclared type in a ccall) | A type did not make it into the module. Usually a missing template, or a header-only struct without -fstandalone-debug. |
| Wrapper generation refused for another reason | These refusals name a RepliBuild limitation, not a mistake in your TOML. [wrap] exclude_symbols leaves the named function out so the rest of the wrapper is written. Please report the case. |
Calling the wrapper
| What you see | What to do |
|---|---|
ABI Safety Trap: cannot call 'f' | RepliBuild could not prove a safe way to call f (a long double, a record it cannot pass by value, or a type whose name several types in the library share, for example), so the wrapper has a stub that raises instead of a call that would corrupt memory. The message says which. The rest of the module works. |
FFI Safety Trap: Cannot call function 'f' | A type in f's signature has no Julia mapping. Name one under [types.custom] (MyHandle = "Ptr{Cvoid}"), then build and wrap again. |
C++ exception: … | The C++ code threw. It arrives as RepliBuild.JITManager.CxxException; catch it like any Julia exception. |
MethodError passing a Ref to a struct parameter | A parameter declared as a struct takes the struct itself. Only a pointer parameter (S*) takes a Ref or a Ptr. |
MethodError passing an Int where the signature says Cdouble or Int32 | Some parameters take exactly one Julia type: floating point in a C++ wrapper, and fixed-width integers such as int32_t. Write 2.0 or Int32(2). The function's signature says which. |
MethodError: … method too new to be called from this world context after use | use defines the module while your code runs. Call use at top level, or call into the module with Base.@invokelatest M.f(x). |
register warns it could not add the package to a Julia environment | The message says why: the target was a package's own Project.toml, the environment already has a different package of that name, or Pkg failed. use still loads it; pass env an environment, or nothing. |
A crash calling a C++ method on T() | T() is a zeroed Julia value, not a constructed C++ object. Construct one in place with the constructor wrapper — Call a wrapper. |
UndefVarError for dispatch_tier or DISPATCH_TIER | They exist only in a wrapper that mixes tiers. A single-tier wrapper names its one tier in a comment instead. |
C vs C++ mix-ups
[wrap] language selects the toolchain, not just the generator. A C project forced to "cpp" needs a system LLVM you may not have. A C++ project forced to "c" will wrap the extern "C" surface and drop classes, methods, and virtual dispatch.
Ingest is C only. Ingesting a C++ .so will not give you a usable class API — build that library from source.
Cache
Per-file IR is keyed on source plus compile fingerprint (flags, defines, includes, LLVM version). Changing flags rebuilds without a manual clean.
The project hash (.replibuild_cache/project_hash) covers the TOML, the sources, the include directories and the toolchain, but not RepliBuild's own version. After an upgrade, build reports cache: project unchanged and keeps the metadata the old version extracted. build(toml; clean=true) re-extracts it, starting from nothing; deleting .replibuild_cache/project_hash re-runs the link and the extraction but keeps the compiled IR.
~/.replibuild/builds/<hash>/ is the use() cache. The hash includes the generator version. clean(toml) removes project-local build/, julia/, and .replibuild_cache/. It does not touch the registry or the use() cache; use(name; force_rebuild=true) rebuilds one registered package from nothing.
Loud vs silent
A typo'd TOML key is accepted and ignored. If a setting "does nothing", check spelling against the section reference before anything else.
Hard errors (the useful kind): bad commit shape, bad [wrap.varargs] shape, a shim include that escaped the tree, a wrapper that would name an undeclared type. Those messages name the fix.