Appendix A
🐛 Common Errors and Solutions
Quick fixes for the most frequent React Native and Expo issues
Red Box Errors
Red box errors appear when there's a JavaScript exception. Here are the most common ones:
❌ "Text strings must be rendered within a <Text> component"
Cause: Raw text outside of a Text component
// ❌ Wrong
<View>
Hello World
</View>
// ✅ Correct
<View>
<Text>Hello World</Text>
</View>
Also check: Conditional rendering that might render empty strings or whitespace.
// ❌ Can cause issues
<View>
{condition && 'Some text'}
</View>
// ✅ Safer
<View>
{condition && <Text>Some text</Text>}
</View>
❌ "Cannot read property 'X' of undefined"
Cause: Accessing properties on undefined objects, often from API responses or navigation params
// ❌ Unsafe
const name = user.profile.name;
// ✅ Safe with optional chaining
const name = user?.profile?.name;
// ✅ Safe with default value
const name = user?.profile?.name ?? 'Unknown';
❌ "Invariant Violation: View config getter callback..."
Cause: Using a component that isn't properly imported or doesn't exist
// ❌ Wrong import
import { Button } from 'react-native'; // Button exists but check spelling
// ❌ Typo in component name
<Veiw></Veiw>
// ✅ Correct
import { View, Button } from 'react-native';
<View></View>
❌ "Objects are not valid as a React child"
Cause: Trying to render an object directly
// ❌ Wrong - rendering object
const user = { name: 'John', age: 30 };
<Text>{user}</Text>
// ✅ Correct - render specific properties
<Text>{user.name}</Text>
// ✅ Or stringify for debugging
<Text>{JSON.stringify(user, null, 2)}</Text>
❌ "Each child in a list should have a unique 'key' prop"
Cause: Missing or duplicate keys in lists
// ❌ Wrong - no key
{items.map(item => <Text>{item.name}</Text>)}
// ❌ Wrong - using index as key (can cause issues)
{items.map((item, index) => <Text key={index}>{item.name}</Text>)}
// ✅ Correct - unique identifier
{items.map(item => <Text key={item.id}>{item.name}</Text>)}
Metro Bundler Issues
⚠️ "Unable to resolve module" or "Module not found"
Solutions (try in order):
- Clear Metro cache and restart:
npx expo start -c - Delete node_modules and reinstall:
rm -rf node_modules npm install - Check the import path is correct (case-sensitive on some systems)
- Verify the package is installed:
npm list package-name
⚠️ Metro bundler stuck or not updating
Solutions:
- Press
rin terminal to reload - Shake device and select "Reload"
- Stop Metro and restart with cache clear:
# Stop with Ctrl+C, then: npx expo start -c - Clear watchman (if installed):
watchman watch-del-all
⚠️ "ENOSPC: System limit for number of file watchers reached"
Cause: Linux/WSL file watcher limit too low
Solution:
# Temporary fix
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Or edit /etc/sysctl.conf directly and add:
fs.inotify.max_user_watches=524288
⚠️ Port 8081 already in use
Solutions:
# Find and kill the process using port 8081
# On macOS/Linux:
lsof -i :8081
kill -9 <PID>
# Or use a different port:
npx expo start --port 8082
Native Dependency Problems
🔧 "Invariant Violation: Native module cannot be null"
Cause: Native module not linked or app not rebuilt
Solutions:
- For Expo managed projects, check if the module requires a development build:
# Create a development build npx expo install expo-dev-client npx expo run:ios # or run:android - For bare projects, rebuild:
# iOS cd ios && pod install && cd .. npx react-native run-ios # Android npx react-native run-android
🔧 iOS Pod Install Failures
Common fixes:
# Update CocoaPods
sudo gem install cocoapods
# Clear pod cache
cd ios
pod cache clean --all
rm -rf Pods Podfile.lock
pod install --repo-update
cd ..
🔧 Android Gradle Build Failures
Common fixes:
# Clean Gradle cache
cd android
./gradlew clean
cd ..
# Clear Gradle cache completely (more thorough)
rm -rf ~/.gradle/caches/
# Check Java version (React Native requires JDK 17)
java -version
🔧 "Duplicate class" or "Duplicate resources" on Android
Solution: Add to android/app/build.gradle:
android {
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
}
}
Build Errors
🏗️ EAS Build Failures
Common causes and solutions:
- SDK version mismatch: Ensure all expo packages match your SDK version
npx expo install --fix - Missing credentials: Run credentials setup
eas credentials - Check build logs: Always read the full error log in the EAS dashboard
🏗️ "SDK version not supported"
Solution: Update to a supported SDK version:
# Check current version
grep "expo" package.json
# Upgrade SDK
npx expo install expo@latest
# Fix dependencies
npx expo install --fix
🏗️ iOS Signing Issues
Solutions:
- For development: Use EAS to manage credentials automatically
eas build --platform ios --profile development - Check Apple Developer account has active membership
- Ensure correct bundle identifier in app.json
Runtime Errors
🔄 Infinite Re-render Loop
Cause: State updates in render or missing useEffect dependencies
// ❌ Wrong - causes infinite loop
function BadComponent() {
const [count, setCount] = useState(0);
setCount(count + 1); // Called every render!
return <Text>{count}</Text>;
}
// ❌ Wrong - effect runs infinitely
useEffect(() => {
setData(fetchData());
}); // Missing dependency array!
// ✅ Correct
useEffect(() => {
setData(fetchData());
}, []); // Empty array = run once
🔄 "Can't perform state update on unmounted component"
Cause: Async operation completing after component unmounts
// ✅ Solution with cleanup
useEffect(() => {
let isMounted = true;
async function fetchData() {
const result = await api.getData();
if (isMounted) {
setData(result);
}
}
fetchData();
return () => {
isMounted = false;
};
}, []);
// ✅ Or use AbortController for fetch
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') throw err;
});
return () => controller.abort();
}, [url]);
🔄 Navigation: "The action 'NAVIGATE' was not handled"
Cause: Trying to navigate to a screen that doesn't exist in current navigator
// Check that:
// 1. Screen name matches exactly (case-sensitive)
// 2. Screen is defined in the navigator
// 3. You're using the correct navigation method
// For nested navigators:
navigation.navigate('ParentStack', {
screen: 'ChildScreen',
params: { id: 123 }
});
🔄 FlatList: Items not updating
Cause: FlatList not detecting data changes
// ❌ Problem: mutating array doesn't trigger update
items.push(newItem);
setItems(items);
// ✅ Solution: create new array reference
setItems([...items, newItem]);
// ✅ Or use extraData prop
<FlatList
data={items}
extraData={selectedId} // Re-render when this changes
renderItem={renderItem}
/>
Expo-Specific Issues
📱 "This app cannot be run in Expo Go"
Cause: Using a package that requires custom native code
Solution: Create a development build:
# Install dev client
npx expo install expo-dev-client
# Build for your device
eas build --profile development --platform ios
# or
eas build --profile development --platform android
# Or run locally
npx expo run:ios
npx expo run:android
📱 Expo Go not connecting to development server
Solutions:
- Ensure phone and computer are on same WiFi network
- Try tunnel mode:
npx expo start --tunnel - Check firewall isn't blocking port 8081
- Try manual URL entry in Expo Go
📱 Assets not loading
Solutions:
// Ensure correct path (from project root)
<Image source={require('./assets/image.png')} />
// For dynamic images, use asset modules
import { Asset } from 'expo-asset';
// Preload assets
await Asset.loadAsync([
require('./assets/image.png'),
]);
📱 "Unmatched route"
Cause: Expo Router can't find the route file
Check:
- File exists in
app/directory - File name matches expected route
- File has a default export
- No syntax errors in the file
Debugging Strategies
🔍 General Debugging Workflow
- Read the error message carefully - it often tells you exactly what's wrong
- Check the component stack - find which component caused the error
- Add console.log statements - track data flow
- Use React DevTools - inspect component props and state
- Check recent changes - what did you change before it broke?
- Search the error - someone else has probably encountered it
🔍 Using Console Logs Effectively
// Label your logs
console.log('User data:', userData);
console.log('Before API call');
console.log('After API call, response:', response);
// Use console.table for arrays/objects
console.table(items);
// Use console.group for related logs
console.group('Render cycle');
console.log('Props:', props);
console.log('State:', state);
console.groupEnd();
// Trace where something is called from
console.trace('This function was called');
🔍 React DevTools
Setup:
# Install standalone DevTools
npm install -g react-devtools
# Run DevTools
react-devtools
# Then start your app
npx expo start
Features:
- Inspect component hierarchy
- View and edit props/state
- Track component renders
- Profile performance
🔍 Network Debugging
// Add request/response logging
const originalFetch = global.fetch;
global.fetch = async (...args) => {
console.log('Fetch request:', args);
const response = await originalFetch(...args);
console.log('Fetch response:', response.status);
return response;
};
// Or use Flipper for comprehensive network debugging
✅ Prevention Checklist
- ☐ Use TypeScript for type safety
- ☐ Add ESLint with React Native rules
- ☐ Write tests for critical paths
- ☐ Use optional chaining (?.) for object access
- ☐ Always handle loading and error states
- ☐ Keep dependencies updated
- ☐ Test on both iOS and Android regularly
Quick Reference Commands
🚀 Common Fix Commands
# Clear everything and start fresh
rm -rf node_modules
rm -rf .expo
npm install
npx expo start -c
# iOS specific
cd ios && pod install --repo-update && cd ..
# Android specific
cd android && ./gradlew clean && cd ..
# Reset Metro bundler
npx expo start -c
# Fix Expo package versions
npx expo install --fix
# Check for issues
npx expo-doctor