Skill: Verification, Compilation & Troubleshooting
Use this guide to compile, test, verify, and troubleshoot your modifications to the library (both JS/TS and native C++).
🛠️ Verification & Compilation Workflows
1. Build and Typecheck TypeScript
To check types and compile the TypeScript source code:
- Verify Types (Fast):
yarn typecheck - Run the TypeScript API Tests:
Jest suites over the publicyarn workspace react-native-executorch testsrc/surface — hooks, task pipelines, core primitives, the fetcher and the model registry — running against a fake native runtime, so they need no simulator, device or.ptefile. See the Add API Tests skill. - Build Bundles:
This cleans target directories and builds modules into theyarn preparelib/directory usingreact-native-builder-bob. - When: After adding/updating TypeScript files under
src/. - Verification: Ensure no compiler or type-checking errors are raised.
2. Native Rebuilding
iOS Development
Whenever you modify native files under cpp/ or update .podspec:
pushd apps/<domain-app>/ios && pod install && popd
- Rebuilding: Re-run the app from within the appropriate app folder:
cd apps/<domain-app> yarn run ios - Clean Build: If files aren't being picked up:
- In Xcode:
Product>Clean Build Folder(Cmd + Shift + K).
- In Xcode:
Android Development
Android compiles C++ source files on-demand during application builds:
cd apps/<domain-app>
yarn run android
- Clean Build: If caching issues occur:
pushd apps/<domain-app>/android && ./gradlew clean && popd
🧹 C++ Static Analysis
Two complementary checks gate C++ quality. Always run both before committing.
1. lint:cpp — clang-tidy
Runs the project's .clang-tidy ruleset (bugprone / performance / clang-analyzer / llvm-include-order) over all cpp/ sources.
# clang-tidy must be installed from Homebrew LLVM — the Xcode bundled version is too old:
CLANG_TIDY=$(brew --prefix llvm)/bin/clang-tidy yarn workspace react-native-executorch lint:cpp
- Requires ExecuTorch headers provisioned under
third-party/includeandyarn install(for JSI headers). - Any finding fails the run (
--warnings-as-errors). Suppress a deliberate, reviewed finding with// NOLINTNEXTLINE(check-name)rather than relaxing the shared config. - Homebrew LLVM is newer than CI's clang-tidy and reports checks CI does not have. Before "fixing" a local finding, copy the unmodified base-branch version of the file in under a temp name and lint that too. A finding that also fires there is pre-existing and is not yours to fix in your PR.
- A local macOS-only syntax check cannot see platform-conditional code. Anything inside
#if defined(__ANDROID__)/#elif defined(__APPLE__)compiles locally and can still fail CI on another target. Check by eye that new includes andusingdeclarations sit at file scope, outside every preprocessor branch.
2. check-cpp-warnings.sh — clangd warning set
Compiles staged (or explicitly passed) cpp/ sources with the same flags clangd uses in the editor (from compile_flags.txt + -W flags in .clangd), so the editor and CI stay in sync. Run from the package root (packages/react-native-executorch/):
# The script takes file paths as positional arguments and exits 0 immediately
# with none — it has no built-in file discovery. Pass files explicitly:
find cpp/ -path cpp/tests -prune -o \( -name '*.cpp' -o -name '*.h' \) -print | \
xargs ./scripts/check-cpp-warnings.sh
The script skips gracefully (exit 0) when no compiler, compile_flags.txt, .clangd, or provisioned headers are found — it never blocks contributors who don't build the native code.
🔍 Debugging & Log Access
1. TypeScript & Worklet Logging
Logging inside worklets (functions annotated with 'worklet';) behaves slightly differently as they run on a separate runtime thread.
- Use standard
console.log(...)inside worklets. - Caution: Worklet logs are piped back to the JS console, but passing complex circular objects to
console.logfrom a worklet can fail or freeze. Stringify or log specific primitive properties instead.
2. Native C++ Debugging
Native JSI crashes, standard outputs, or ExecuTorch errors can be caught in the native log streams:
- iOS: Watch outputs directly inside the Xcode Console while running the app target.
- Android: Use Android Studio's logcat or run:
adb logcat *:S ReactNative:V ReactNativeJS:V rnexecutorch:V
🚦 Common JSI Troubleshooting
Symptom: Error: JSI global object '__rnexecutorch_jsi__' is not registered.
- Cause 1: The native code wasn't linked or JSI installation failed during loading.
- Fix: Verify that you have registered your new extension/module in cpp/RnExecutorch.cpp.
- Fix: Re-run
pod install(for iOS) or perform a Gradle sync/clean (for Android) to ensure your C++ changes compiled.
- Cause 2: The bridge did not invoke the native installation.
- Fix: Ensure the JS entrypoint triggers the load (see src/native/bridge.ts).
Symptom: TypeError: Cannot read property '<extension>' of undefined
- Cause: You added an extension in C++ but forgot to register it in your TypeScript bridge bindings.
- Fix: Check that
module.setProperty(rt, "<extension>", subModule)is called in your nativeinstall.cppand that your TS wrapper exports and references it underrnexecutorchJsi.<extension>.
- Fix: Check that
📂 Model Hosting & Download Caching
This project does not bundle local .pte model files inside the React Native application package. Instead, the standard workflow is:
Upload to Hugging Face:
- Upload compiled/exported
.ptemodels directly to our Hugging Face repository bucket. - Naming Convention: All
.ptefiles must follow the strict name contract:modelname_optionalsize_backend_precision.pte- Example:
efficientnet_v2_s_xnnpack_int8.pte - Example:
style_transfer_candy_xnnpack_fp32.pte
- Example:
- Upload compiled/exported
Define in Models Manifest (src/models.ts):
Single Export Registry Rule: Define the new model configuration as an internal (private)
constinsidemodels.ts. Do not export individual model configuration constants. Instead, expose them solely by registering them under the appropriate category nested inside the mainmodelsregistry object, which is the only exported symbol frommodels.ts.Example:
// Defined internally (private) const MY_MODEL_XNNPACK_FP32: MyTaskModel = { ... }; // Registered in the exported models object export const models = { ... myTask: { MY_MODEL: { XNNPACK_FP32: MY_MODEL_XNNPACK_FP32, } } };
Runtime Downloading & Caching:
- The custom hook
useResourceDownload(config.modelPath)handles checks automatically:- If the model file is not already in the device cache directory, it downloads it from the Hugging Face URL to
RNFS.CachesDirectoryPath. - If the model file exists in the cache, it bypasses the download and returns the cached local path immediately to load the model.
- If the model file is not already in the device cache directory, it downloads it from the Hugging Face URL to
- The custom hook
🚫 Avoid / Anti-Patterns
- Do NOT run code without verification: Do not test TypeScript changes in the app without first running
yarn typecheck(verify types),yarn workspace react-native-executorch test(API suites) andyarn prepare(build target bundles). - Do NOT skip native rebuilds after C++ edits: If any C++ files or config bindings are added/modified, do not attempt to run the app without executing
pod install(for iOS) or letting Gradle sync (for Android). - Do NOT run
lint:cppwith the systemclang-tidy: Use the Homebrew LLVM binary:CLANG_TIDY=$(brew --prefix llvm)/bin/clang-tidy yarn workspace react-native-executorch lint:cpp. - Do NOT trust a green macOS-only syntax check for platform-conditional C++: see the clang-tidy notes above.
- Do NOT log complex objects inside worklets: Avoid passing complex circular objects directly to
console.log()inside functions annotated with'worklet';as it can hang or crash the worklet runtime thread. - Do NOT bundle local
.ptefiles in the repository: Do not commit heavy model binaries to the git repository. Always host them on Hugging Face and register their metadata insrc/models.ts.
📋 Verification Checklist
When verifying or compiling your modifications, check that:
- TypeScript typechecking passes without errors (
yarn typecheck). - The TypeScript API tests pass (
yarn workspace react-native-executorch test), and any newsrc/behavior is covered by them. - Bundles compile successfully (
yarn prepare). -
pod installhas been run insideapps/<domain-app>/ios/after any native C++ edits. -
lint:cpppasses cleanly:CLANG_TIDY=$(brew --prefix llvm)/bin/clang-tidy yarn workspace react-native-executorch lint:cpp. -
check-cpp-warnings.shpasses cleanly (run viafind cpp/ -name '*.cpp' -o -name '*.h' | xargs ./scripts/check-cpp-warnings.shfrom the package root). - No
.ptefiles are staged or added to git history. - Model configurations are registered inside the single
modelsobject registry insrc/models.ts. - Native runtime issues have been investigated using Xcode Console or
adb logcat.