1k-patching-native-modules
SKILL.md
Patching Native Modules
Follow this workflow to analyze crash logs, fix native module bugs, and generate patches.
Workflow Overview
1. Analyze Crash Log → 2. Locate Bug → 3. Fix Code → 4. Clean Build Artifacts → 5. Generate Patch → 6. Commit & PR
Step 1: Analyze Crash Log
iOS Crash (EXC_BAD_ACCESS / KERN_INVALID_ADDRESS)
Key information to extract:
- Exception type:
EXC_BAD_ACCESS,SIGABRT, etc. - Stack trace: Identify the crashing function
- Memory address: Helps identify nil pointer issues
- Library: Which native module is crashing
Example crash pattern:
EXC_BAD_ACCESS: KERN_INVALID_ADDRESS at 0x3c61c1a3b0d0
objc_msgSend in unknown file
-[SDWebImageManager cacheKeyForURL:context:] ← Crashing function
-[SDWebImageManager loadImageWithURL:options:context:progress:completed:]
Android Crash (NullPointerException / OOM)
Look for:
- Exception class:
NullPointerException,OutOfMemoryError - Stack trace: Java/Kotlin method chain
- Thread info: Main thread vs background
Step 2: Locate the Bug
Find native module source
# iOS (Swift/Objective-C)
ls node_modules/<package>/ios/
# Android (Kotlin/Java)
ls node_modules/<package>/android/src/main/java/
Common crash causes
| Crash Type | Common Cause | Fix Pattern |
|---|---|---|
EXC_BAD_ACCESS |
Nil pointer dereference | Add guard let check |
KERN_INVALID_ADDRESS |
Accessing deallocated memory | Use weak references |
NullPointerException |
Null object access | Add null check |
OutOfMemoryError |
Large image/data processing | Add size limits |
Step 3: Fix the Code
iOS (Swift) - Nil Check Pattern
// Before (crashes when uri is nil)
imageManager.loadImage(with: source.uri, ...)
// After (safe)
guard let sourceUri = source.uri, !sourceUri.absoluteString.isEmpty else {
onError(["error": "Image source URI is nil or empty"])
return
}
imageManager.loadImage(with: sourceUri, ...)
Android (Kotlin) - Null Check Pattern
// Before
val uri = source.uri
loadImage(uri)
// After
val uri = source.uri ?: return
if (uri.toString().isEmpty()) return
loadImage(uri)
Step 4: Clean Build Artifacts (CRITICAL)
Before generating patch, MUST clean Android build cache:
# Remove Android build artifacts to avoid polluting the patch
rm -rf node_modules/<package>/android/build
# For expo-image specifically:
rm -rf node_modules/expo-image/android/build
Why this matters:
- Android build generates
.class,.jar, binary files - These pollute the patch file (can grow to 5000+ lines)
- patch-package will include these unwanted files
Step 5: Generate Patch
# Generate patch file
npx patch-package <package-name>
# Example:
npx patch-package expo-image
Patch file location: patches/<package-name>+<version>.patch
Verify patch content
# Check patch doesn't include unwanted files
grep -c "android/build" patches/<package-name>*.patch
# Should return 0
# View actual changes
head -100 patches/<package-name>*.patch
Step 6: Commit & Create PR
# Stage patch file
git add patches/<package-name>*.patch
# Commit with descriptive message
git commit -m "fix(ios): prevent EXC_BAD_ACCESS crash in <package> when <condition>
Add guard checks in <package> native layer to prevent crash when <scenario>.
Fixes Sentry issue #XXXXX"
# Create PR
gh pr create --title "fix(ios): <description>" --base x
Common Packages & Their Native Locations
| Package | iOS Source | Android Source |
|---|---|---|
expo-image |
node_modules/expo-image/ios/ |
node_modules/expo-image/android/src/ |
react-native |
node_modules/react-native/React/ |
node_modules/react-native/ReactAndroid/ |
@react-native-async-storage/async-storage |
node_modules/@react-native-async-storage/async-storage/ios/ |
...android/src/ |
react-native-reanimated |
node_modules/react-native-reanimated/ios/ |
...android/src/ |
Existing Patches Reference
Check existing patches for patterns:
ls patches/
cat patches/expo-image+3.0.10.patch
Troubleshooting
Patch file too large
# Clean all build artifacts
rm -rf node_modules/<package>/android/build
rm -rf node_modules/<package>/ios/build
rm -rf node_modules/<package>/.gradle
# Regenerate
npx patch-package <package>
Patch not applying
# Check package version matches
cat node_modules/<package>/package.json | grep version
# Rename patch if version changed
mv patches/<package>+old.patch patches/<package>+new.patch
Swift/Kotlin syntax help
Swift guard let:
guard let value = optionalValue else {
return // Must exit scope
}
// value is now non-optional
Kotlin null check:
val value = nullableValue ?: return
// value is now non-null
Related Files
- Patches directory:
patches/ - expo-image iOS:
node_modules/expo-image/ios/ImageView.swift - expo-image Android:
node_modules/expo-image/android/src/main/java/expo/modules/image/
Weekly Installs
45
Repository
onekeyhq/app-monorepoGitHub Stars
2.3K
First Seen
Feb 1, 2026
Security Audits
Installed on
opencode45
gemini-cli45
github-copilot45
codex45
cursor45
kimi-cli44