@buivietphi/skill-mobile-mt 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +56 -38
- package/README.md +83 -47
- package/SKILL.md +604 -50
- package/package.json +1 -1
- package/shared/bug-detection.md +411 -27
- package/shared/code-review.md +899 -37
- package/shared/debugging-intelligence.md +787 -0
- package/shared/i18n-localization.md +426 -0
- package/shared/prompt-engineering.md +272 -21
- package/shared/storage-patterns.md +312 -0
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
# Debugging Intelligence — Deep Pattern Database
|
|
2
|
+
|
|
3
|
+
> On-demand. Load when: complex bugs, long stack traces, issue investigation, multi-error scenarios.
|
|
4
|
+
> This file contains 30+ real error patterns with EXACT search strategies.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Platform Focus Rule
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
⛔ ONLY use error patterns for the DETECTED platform. Don't scan all categories.
|
|
12
|
+
|
|
13
|
+
REACT NATIVE → Category A (RN crashes) + B + C + D + E + F1
|
|
14
|
+
FLUTTER → Category A2 (Flutter crashes) + B4 + C + D + F3
|
|
15
|
+
iOS NATIVE → Category A3 (iOS crashes) + C + D + F1
|
|
16
|
+
ANDROID → Category A4 (Android crashes) + B3 + C + D + F2
|
|
17
|
+
|
|
18
|
+
Cross-platform categories (B build, C network, D state, E navigation):
|
|
19
|
+
→ Use the platform-specific SEARCH strategy within each category
|
|
20
|
+
→ e.g., C1 Network error: RN → check axios/fetch, Flutter → check http/dio,
|
|
21
|
+
iOS → check URLSession, Android → check Retrofit/OkHttp
|
|
22
|
+
|
|
23
|
+
CROSS-REFERENCE native ONLY WHEN:
|
|
24
|
+
→ Stack trace exits JS/Dart layer into native (Java/Swift/ObjC)
|
|
25
|
+
→ Error message is from native runtime (not Metro/Dart VM)
|
|
26
|
+
→ User explicitly says the bug is in native code
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Error Pattern Database
|
|
32
|
+
|
|
33
|
+
### Category A: React Native Runtime Crashes
|
|
34
|
+
|
|
35
|
+
#### A1. "undefined is not an object (evaluating 'X.Y')"
|
|
36
|
+
```
|
|
37
|
+
CAUSE: X is null/undefined when accessing property Y
|
|
38
|
+
SEARCH: Grep "X" in src/ → find where X is defined/assigned → check null cases
|
|
39
|
+
COMMON:
|
|
40
|
+
- API response missing field → data?.field instead of data.field
|
|
41
|
+
- Navigation params undefined → route.params?.id
|
|
42
|
+
- State not initialized → useState(null) accessed before set
|
|
43
|
+
- Unmounted component accessing state
|
|
44
|
+
FIX: Add optional chaining (X?.Y) OR null guard (if (!X) return)
|
|
45
|
+
VERIFY: Check ALL places X is used (grep) → apply same fix
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### A2. "Cannot read property 'X' of undefined" / "Cannot read properties of null"
|
|
49
|
+
```
|
|
50
|
+
CAUSE: Same as A1 (V8/Hermes variant)
|
|
51
|
+
SEARCH: Same strategy — Grep the variable name in src/
|
|
52
|
+
NOTE: In Hermes, the variable name may not be in error → check stack trace for file:line
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### A3. "TypeError: X is not a function"
|
|
56
|
+
```
|
|
57
|
+
CAUSE: X is imported wrong, or the object doesn't have method X
|
|
58
|
+
SEARCH: Grep "X" in src/ → check the import statement → check the module's exports
|
|
59
|
+
COMMON:
|
|
60
|
+
- Default vs named import mismatch: import X vs import { X }
|
|
61
|
+
- Calling method on wrong object: obj.X() but X is on obj.child
|
|
62
|
+
- Library updated with breaking API change
|
|
63
|
+
- Circular import making X undefined at call time
|
|
64
|
+
FIX: Fix import OR check object shape with console.log(typeof X)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### A4. "Invariant Violation: Element type is invalid"
|
|
68
|
+
```
|
|
69
|
+
CAUSE: React component imported incorrectly (got undefined instead of component)
|
|
70
|
+
SEARCH: Find the component import → check its source file's export
|
|
71
|
+
COMMON:
|
|
72
|
+
- export default vs export { Component } mismatch
|
|
73
|
+
- File path typo in import
|
|
74
|
+
- Circular import (A imports B imports A → one becomes undefined)
|
|
75
|
+
- Re-exporting from index.ts but forgot to add new component
|
|
76
|
+
FIX: Fix export/import → clear Metro cache → restart
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### A5. "Maximum call stack size exceeded"
|
|
80
|
+
```
|
|
81
|
+
CAUSE: Infinite loop or infinite recursion
|
|
82
|
+
SEARCH: Find the function from stack trace → check for:
|
|
83
|
+
- useEffect with wrong dependency array (causes infinite re-render)
|
|
84
|
+
- State update inside render body (not in handler/effect)
|
|
85
|
+
- Recursive function without base case
|
|
86
|
+
- Component renders itself without stop condition
|
|
87
|
+
COMMON:
|
|
88
|
+
- useEffect(() => { setState(x) }, [x]) ← x changes → effect runs → sets x → loop
|
|
89
|
+
- componentDidUpdate without shouldComponentUpdate check
|
|
90
|
+
FIX: Fix dependency array OR add base case OR memoize
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### A6. "VirtualizedList: You have a large list that is slow to update"
|
|
94
|
+
```
|
|
95
|
+
CAUSE: FlatList re-rendering entire list on every state change
|
|
96
|
+
SEARCH: Find the FlatList component → check renderItem + keyExtractor
|
|
97
|
+
COMMON:
|
|
98
|
+
- Missing keyExtractor (uses index by default → full re-render)
|
|
99
|
+
- renderItem creates new function/object every render
|
|
100
|
+
- Data source reference changes on every render
|
|
101
|
+
FIX:
|
|
102
|
+
→ Add keyExtractor with stable unique ID
|
|
103
|
+
→ Wrap renderItem in useCallback
|
|
104
|
+
→ Memoize list item component with React.memo
|
|
105
|
+
→ Use getItemLayout if fixed height
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### A7. "Unhandled promise rejection"
|
|
109
|
+
```
|
|
110
|
+
CAUSE: Async function threw error without try/catch
|
|
111
|
+
SEARCH: Find the async function from stack trace → check error handling
|
|
112
|
+
COMMON:
|
|
113
|
+
- fetch/axios call without try/catch
|
|
114
|
+
- Promise chain without .catch()
|
|
115
|
+
- async function in useEffect without error handler
|
|
116
|
+
FIX: Wrap in try/catch → show user-facing error → log for debugging
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### Category A2: Flutter Runtime Crashes
|
|
122
|
+
|
|
123
|
+
#### A2-1. "setState() called after dispose()"
|
|
124
|
+
```
|
|
125
|
+
CAUSE: Async callback completes after widget unmounted, calls setState
|
|
126
|
+
SEARCH: Find the widget from stack trace → check async callbacks
|
|
127
|
+
COMMON:
|
|
128
|
+
- Future.then() callback without mounted check
|
|
129
|
+
- Timer callback after screen popped
|
|
130
|
+
- Stream listener not cancelled in dispose()
|
|
131
|
+
FIX: Add `if (!mounted) return;` before EVERY setState after async gap
|
|
132
|
+
VERIFY: Check ALL async callbacks in the widget → add mounted guard
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### A2-2. "RenderFlex overflowed by X pixels"
|
|
136
|
+
```
|
|
137
|
+
CAUSE: Child widget exceeds parent constraint
|
|
138
|
+
SEARCH: Find the widget from error → check its parent Row/Column layout
|
|
139
|
+
COMMON:
|
|
140
|
+
- Text too long in Row without Flexible/Expanded
|
|
141
|
+
- Image with fixed size in constrained container
|
|
142
|
+
- Column inside Column without Expanded
|
|
143
|
+
FIX: Wrap with Flexible/Expanded OR add overflow: TextOverflow.ellipsis
|
|
144
|
+
OR wrap with SingleChildScrollView
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### A2-3. "A build function returned null"
|
|
148
|
+
```
|
|
149
|
+
CAUSE: Switch/if in build() doesn't cover all cases → returns null
|
|
150
|
+
SEARCH: Find the widget's build method → check conditional branches
|
|
151
|
+
COMMON:
|
|
152
|
+
- Enum switch without default case
|
|
153
|
+
- if/else if without final else
|
|
154
|
+
- Late variable not initialized before build
|
|
155
|
+
FIX: Ensure ALL branches return a Widget, add default/else case
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### A2-4. "Looking up a deactivated widget's ancestor is unsafe"
|
|
159
|
+
```
|
|
160
|
+
CAUSE: Using BuildContext after async gap (context may be deactivated)
|
|
161
|
+
SEARCH: Find the async function → check context usage after await
|
|
162
|
+
COMMON:
|
|
163
|
+
- Navigator.of(context).pop() after await
|
|
164
|
+
- ScaffoldMessenger.of(context) after async call
|
|
165
|
+
- showDialog after await
|
|
166
|
+
FIX: Store navigator/messenger BEFORE await, OR check mounted after await
|
|
167
|
+
VERIFY: Check ALL context usage after await in the widget
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### A2-5. "type 'Null' is not a subtype of type 'X'"
|
|
171
|
+
```
|
|
172
|
+
CAUSE: Null value assigned to non-nullable variable (sound null safety violation)
|
|
173
|
+
SEARCH: Find the variable from stack trace → trace where null comes from
|
|
174
|
+
COMMON:
|
|
175
|
+
- JSON parsing: map['key'] as String (but key is absent → null)
|
|
176
|
+
- API response missing expected field
|
|
177
|
+
- Type cast: (object as String) when object is null
|
|
178
|
+
FIX: Use null-safe parsing: map['key'] as String? ?? 'default'
|
|
179
|
+
OR fix fromJson factory to handle missing fields
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### A2-6. "Unhandled Exception: Bad state: Stream has already been listened to"
|
|
183
|
+
```
|
|
184
|
+
CAUSE: Single-subscription Stream listened to more than once
|
|
185
|
+
SEARCH: Find the StreamController → check how many widgets listen to it
|
|
186
|
+
COMMON:
|
|
187
|
+
- StreamController (default = single-subscription) used by multiple widgets
|
|
188
|
+
- Hot reload re-subscribes without cancelling previous
|
|
189
|
+
FIX: Use StreamController.broadcast() for multiple listeners
|
|
190
|
+
OR ensure single listener with StreamSubscription tracking
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### Category A3: iOS Swift Runtime Crashes
|
|
196
|
+
|
|
197
|
+
#### A3-1. "Fatal error: Unexpectedly found nil while unwrapping an Optional value"
|
|
198
|
+
```
|
|
199
|
+
CAUSE: Force unwrap (!) on nil value
|
|
200
|
+
SEARCH: Find file:line from crash log → check the ! operator
|
|
201
|
+
COMMON:
|
|
202
|
+
- IBOutlet not connected in storyboard
|
|
203
|
+
- UserDefaults.string(forKey:)! for missing key
|
|
204
|
+
- as! cast that fails
|
|
205
|
+
- Implicitly unwrapped optional (!) on property not set before access
|
|
206
|
+
FIX: Replace ! with guard let / if let / ?? default
|
|
207
|
+
VERIFY: Grep "!" in the file → check ALL force unwraps
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### A3-2. "EXC_BAD_ACCESS (code=1/2)" / SIGSEGV
|
|
211
|
+
```
|
|
212
|
+
CAUSE: Accessing deallocated memory (dangling pointer / use-after-free)
|
|
213
|
+
SEARCH: Enable Zombie Objects in Xcode → reproduce → find deallocated object
|
|
214
|
+
COMMON:
|
|
215
|
+
- Strong reference to self in closure → VC deallocated but closure keeps pointer
|
|
216
|
+
- Accessing unowned self after object deallocated
|
|
217
|
+
- Core Data object accessed from wrong thread
|
|
218
|
+
- C/ObjC interop with wrong pointer management
|
|
219
|
+
FIX: Use [weak self] instead of [unowned self] if lifetime uncertain
|
|
220
|
+
Use perform() for Core Data thread safety
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### A3-3. "Thread 1: signal SIGABRT" / "NSInternalInconsistencyException"
|
|
224
|
+
```
|
|
225
|
+
CAUSE: Assertion failure in UIKit/Foundation
|
|
226
|
+
SEARCH: Read "reason:" line in crash log → search for that assertion
|
|
227
|
+
COMMON:
|
|
228
|
+
- UITableView: batch update mismatch (insert/delete count != data count)
|
|
229
|
+
- Storyboard: IBOutlet or segue identifier not found
|
|
230
|
+
- Auto Layout: conflicting constraints
|
|
231
|
+
- Collection view: invalid number of items in section
|
|
232
|
+
FIX: Fix data source consistency → diffable data source recommended
|
|
233
|
+
Fix storyboard connections → check identifier spelling
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### A3-4. "Modifications to the layout engine must not be performed from a background thread"
|
|
237
|
+
```
|
|
238
|
+
CAUSE: UI update called from background thread
|
|
239
|
+
SEARCH: Find the code from stack trace → check which queue it runs on
|
|
240
|
+
COMMON:
|
|
241
|
+
- URLSession completion handler (runs on background by default)
|
|
242
|
+
- NotificationCenter observer callback
|
|
243
|
+
- DispatchQueue.global() { self.label.text = "..." }
|
|
244
|
+
FIX: Wrap UI updates: DispatchQueue.main.async { ... }
|
|
245
|
+
OR use @MainActor on the function
|
|
246
|
+
OR use MainActor.run { ... } in async context
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### A3-5. "Cannot decode" / "DecodingError.keyNotFound"
|
|
250
|
+
```
|
|
251
|
+
CAUSE: JSON response doesn't match Codable struct
|
|
252
|
+
SEARCH: Find the Codable struct → compare with actual API response
|
|
253
|
+
COMMON:
|
|
254
|
+
- Backend added new required field → old Codable struct missing it
|
|
255
|
+
- Backend returns null for field marked as non-optional
|
|
256
|
+
- Backend changed field type (string → number)
|
|
257
|
+
- Snake_case vs camelCase mismatch without CodingKeys
|
|
258
|
+
FIX: Mark uncertain fields as Optional → provide CodingKeys if naming differs
|
|
259
|
+
Use custom init(from:) with try? for graceful degradation
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### Category A4: Android Kotlin Runtime Crashes
|
|
265
|
+
|
|
266
|
+
#### A4-1. "java.lang.NullPointerException" / "KotlinNullPointerException"
|
|
267
|
+
```
|
|
268
|
+
CAUSE: Null access — !! force unwrap, Java interop, or platform type
|
|
269
|
+
SEARCH: Find file:line from stack trace → check the null source
|
|
270
|
+
COMMON:
|
|
271
|
+
- intent.extras!!.getString("key") → extras is null from deep link
|
|
272
|
+
- findViewById<TextView>(R.id.title)!! → wrong layout inflated
|
|
273
|
+
- Java library returning null where Kotlin expects non-null (platform types)
|
|
274
|
+
- binding.textView after onDestroyView (binding is null)
|
|
275
|
+
FIX: Replace !! with ?. ?: default OR requireNotNull with message
|
|
276
|
+
Use _binding pattern for Fragment view binding
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### A4-2. "java.lang.IllegalStateException: Fragment not attached to an activity"
|
|
280
|
+
```
|
|
281
|
+
CAUSE: Fragment operation after detach (network callback, delayed handler)
|
|
282
|
+
SEARCH: Find the Fragment from stack trace → check lifecycle state
|
|
283
|
+
COMMON:
|
|
284
|
+
- requireContext() in coroutine that outlives Fragment
|
|
285
|
+
- requireActivity() in async callback
|
|
286
|
+
- getString(R.string.x) after detach
|
|
287
|
+
FIX: Use context (nullable) instead of requireContext()
|
|
288
|
+
Check isAdded before Fragment operations
|
|
289
|
+
Use viewLifecycleOwner.lifecycleScope for coroutines
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### A4-3. "android.os.TransactionTooLargeException"
|
|
293
|
+
```
|
|
294
|
+
CAUSE: Bundle/Intent data > 500KB (Binder transaction limit ~1MB shared)
|
|
295
|
+
SEARCH: Find what data is passed via Intent/Bundle → check size
|
|
296
|
+
COMMON:
|
|
297
|
+
- Passing large Parcelable object between Activities
|
|
298
|
+
- SavedInstanceState accumulating data over config changes
|
|
299
|
+
- Fragment arguments with large list
|
|
300
|
+
FIX: Pass ID only → load data in destination from DB/API
|
|
301
|
+
Use ViewModel for data sharing between Fragments
|
|
302
|
+
Clear savedInstanceState in onSaveInstanceState if too large
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### A4-4. "java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState"
|
|
306
|
+
```
|
|
307
|
+
CAUSE: Fragment transaction after onSaveInstanceState (state loss)
|
|
308
|
+
SEARCH: Find the Fragment transaction → check when it's called
|
|
309
|
+
COMMON:
|
|
310
|
+
- commitNow() in async callback that fires after onPause
|
|
311
|
+
- show()/hide() dialog after Activity paused
|
|
312
|
+
- Navigation action after onStop
|
|
313
|
+
FIX: Use commitAllowingStateLoss() (last resort)
|
|
314
|
+
Better: check lifecycle state before transaction
|
|
315
|
+
Best: use Navigation component (handles this automatically)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
#### A4-5. "android.view.WindowManager$BadTokenException"
|
|
319
|
+
```
|
|
320
|
+
CAUSE: Showing dialog/toast with invalid Activity context
|
|
321
|
+
SEARCH: Find the dialog/toast creation → check Activity lifecycle
|
|
322
|
+
COMMON:
|
|
323
|
+
- Show AlertDialog after Activity finished
|
|
324
|
+
- Toast with Activity context after destroy
|
|
325
|
+
- PopupWindow with destroyed Activity reference
|
|
326
|
+
FIX: Check !isFinishing && !isDestroyed before showing dialog
|
|
327
|
+
Use applicationContext for Toast (not Activity context)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### A4-6. "java.util.ConcurrentModificationException"
|
|
331
|
+
```
|
|
332
|
+
CAUSE: Modifying collection while iterating over it
|
|
333
|
+
SEARCH: Find the collection from stack trace → check for/forEach loops
|
|
334
|
+
COMMON:
|
|
335
|
+
- for (item in list) { list.remove(item) }
|
|
336
|
+
- LiveData/Flow emitting new list while observer iterates old
|
|
337
|
+
- Multiple coroutines modifying same MutableList
|
|
338
|
+
FIX: Use toList() copy for iteration → modify original
|
|
339
|
+
Use CopyOnWriteArrayList for concurrent access
|
|
340
|
+
Use Mutex for coroutine synchronization
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
### Category B: Build Errors
|
|
346
|
+
|
|
347
|
+
#### B1. "Module not found: Can't resolve 'X'"
|
|
348
|
+
```
|
|
349
|
+
⚠️ SEARCH ORDER IS DIFFERENT FOR BUILD ERRORS:
|
|
350
|
+
1. package.json → is X installed? → npm install X
|
|
351
|
+
2. tsconfig.json → paths alias configured? → check paths mapping
|
|
352
|
+
3. src/ imports → typo in import path? → fix path
|
|
353
|
+
4. node_modules/ → X exists but wrong version? → update
|
|
354
|
+
DO NOT search src/ first — this is a dependency/config issue
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### B2. "Type 'X' is not assignable to type 'Y'"
|
|
358
|
+
```
|
|
359
|
+
CAUSE: TypeScript type mismatch
|
|
360
|
+
SEARCH:
|
|
361
|
+
1. Find type/interface Y definition → Grep "interface Y" or "type Y" in src/
|
|
362
|
+
2. Find where X is created → check what shape it actually has
|
|
363
|
+
3. Compare actual vs expected → find the mismatch
|
|
364
|
+
COMMON:
|
|
365
|
+
- API response shape changed but types not updated
|
|
366
|
+
- Optional field treated as required (add ?)
|
|
367
|
+
- Union type not narrowed (add type guard)
|
|
368
|
+
FIX: Fix the type to match reality OR fix the data to match the type
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### B3. "Duplicate class found in modules"
|
|
372
|
+
```
|
|
373
|
+
SEARCH: android/app/build.gradle → dependency tree
|
|
374
|
+
→ ./gradlew app:dependencies | grep "[class-name]"
|
|
375
|
+
DO NOT search src/ — this is purely a Gradle dependency issue
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### B4. "Pod install failed" / "CocoaPods could not find compatible versions"
|
|
379
|
+
```
|
|
380
|
+
SEARCH: ios/Podfile → check version constraints
|
|
381
|
+
→ pod outdated → find conflicting pods
|
|
382
|
+
→ Podfile.lock → check locked versions
|
|
383
|
+
DO NOT search src/ — this is iOS dependency management
|
|
384
|
+
FIX: pod deintegrate → rm Podfile.lock → pod install --repo-update
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### Category C: Network / API Errors
|
|
390
|
+
|
|
391
|
+
#### C1. "Network request failed" / HTTP 0
|
|
392
|
+
```
|
|
393
|
+
SEARCH:
|
|
394
|
+
1. Find API base URL → Grep "baseURL" or "BASE_URL" in src/
|
|
395
|
+
2. Find .env file → check API URL value
|
|
396
|
+
3. Find the failing endpoint → Grep the endpoint path in src/
|
|
397
|
+
4. Check interceptors → Grep "interceptor" in src/
|
|
398
|
+
COMMON:
|
|
399
|
+
- Wrong BASE_URL in .env (localhost on device → use IP)
|
|
400
|
+
- SSL certificate issue (dev server with self-signed cert)
|
|
401
|
+
- Missing internet permission (Android: AndroidManifest.xml)
|
|
402
|
+
- Request interceptor throwing error before request is sent
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### C2. HTTP 401 Unauthorized
|
|
406
|
+
```
|
|
407
|
+
SEARCH:
|
|
408
|
+
1. Find auth token storage → Grep "token" or "accessToken" in src/
|
|
409
|
+
2. Find auth interceptor → Grep "Authorization" in src/
|
|
410
|
+
3. Find token refresh logic → Grep "refresh" in src/
|
|
411
|
+
COMMON:
|
|
412
|
+
- Token expired but refresh not implemented
|
|
413
|
+
- Token stored but not attached to request headers
|
|
414
|
+
- Token refresh race condition (multiple 401 → multiple refresh calls)
|
|
415
|
+
FIX: Check token exists → check it's attached → check expiry → add refresh
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
#### C3. HTTP 403 Forbidden
|
|
419
|
+
```
|
|
420
|
+
SEARCH: Find the API call → check headers + auth token + user role
|
|
421
|
+
COMMON:
|
|
422
|
+
- Token valid but user lacks permission (role/scope issue)
|
|
423
|
+
- CSRF token missing (web views)
|
|
424
|
+
- API key vs OAuth token confusion
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
#### C4. HTTP 500 Internal Server Error
|
|
428
|
+
```
|
|
429
|
+
SEARCH: Find the API call → check request payload shape
|
|
430
|
+
COMMON:
|
|
431
|
+
- Sending wrong data shape (missing required field)
|
|
432
|
+
- Backend bug (not your mobile code) → check backend logs
|
|
433
|
+
- Payload too large (file upload without compression)
|
|
434
|
+
NOTE: If backend is also your project → search backend src/ for the endpoint handler
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
### Category D: State / Data Errors
|
|
440
|
+
|
|
441
|
+
#### D1. "Screen shows old/stale data"
|
|
442
|
+
```
|
|
443
|
+
SEARCH:
|
|
444
|
+
1. Find the data-fetching function → Grep "fetch" or "query" or "useQuery" in screens/
|
|
445
|
+
2. Find the cache/state management → Grep "store" or "cache" in src/
|
|
446
|
+
3. Check refetch triggers → when does data refresh?
|
|
447
|
+
COMMON:
|
|
448
|
+
- Cache not invalidated after mutation
|
|
449
|
+
- Navigation doesn't trigger re-fetch (useFocusEffect missing)
|
|
450
|
+
- Optimistic update didn't sync with server
|
|
451
|
+
FIX: Add refetch on focus OR invalidate cache after mutation OR add pull-to-refresh
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### D2. "State updates not reflecting in UI"
|
|
455
|
+
```
|
|
456
|
+
SEARCH: Find the component → check state management
|
|
457
|
+
COMMON:
|
|
458
|
+
- Mutating state directly: state.items.push(x) instead of setState([...state.items, x])
|
|
459
|
+
- Object reference not changing: setState(sameObj) → React skips re-render
|
|
460
|
+
- Zustand/Redux selector not selecting the changed field
|
|
461
|
+
- Async state update batching issue
|
|
462
|
+
FIX: Always create NEW references for state updates
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### D3. "Race condition — UI flickers / wrong data appears briefly"
|
|
466
|
+
```
|
|
467
|
+
SEARCH: Find the component → check useEffect deps + async calls
|
|
468
|
+
COMMON:
|
|
469
|
+
- Two async calls racing, slower one overwrites faster result
|
|
470
|
+
- State update from previous screen applied to current screen
|
|
471
|
+
- Optimistic update visible before server confirms
|
|
472
|
+
FIX: AbortController for fetch → cancel previous request on new one
|
|
473
|
+
OR use unique request ID → ignore stale responses
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
### Category E: Navigation Errors
|
|
479
|
+
|
|
480
|
+
#### E1. "Screen not found / Route not registered"
|
|
481
|
+
```
|
|
482
|
+
SEARCH:
|
|
483
|
+
1. Grep the screen name in src/navigation/ or src/router/
|
|
484
|
+
2. Check ALL navigator files → is the screen registered?
|
|
485
|
+
3. Grep "navigate(" or "push(" in src/ → find where it's called
|
|
486
|
+
COMMON:
|
|
487
|
+
- Screen name typo: "ProductDetail" vs "ProductDetails"
|
|
488
|
+
- Screen defined in wrong navigator (nested navigators)
|
|
489
|
+
- Screen conditionally rendered (auth gate) but user not authenticated
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
#### E2. "Navigation params undefined"
|
|
493
|
+
```
|
|
494
|
+
SEARCH: Find the navigate call → check what params are passed → find the target screen → check what it expects
|
|
495
|
+
COMMON:
|
|
496
|
+
- Forgot to pass params: navigate('Screen') instead of navigate('Screen', { id })
|
|
497
|
+
- Param name mismatch: { itemId } vs route.params.id
|
|
498
|
+
- Deep link opens screen without params → add default/fallback
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
### Category F: Platform-Specific Errors
|
|
504
|
+
|
|
505
|
+
#### F1. iOS: "App crashes only on real device (works on simulator)"
|
|
506
|
+
```
|
|
507
|
+
SEARCH:
|
|
508
|
+
1. Check Xcode console logs on real device (connect + run)
|
|
509
|
+
2. Grep for simulator-only code: __DEV__, Platform.OS checks
|
|
510
|
+
COMMON:
|
|
511
|
+
- Missing privacy permission (camera, location, photos → Info.plist)
|
|
512
|
+
- Keychain access restricted on device (not available in simulator mode)
|
|
513
|
+
- ARM vs x86 architecture issue
|
|
514
|
+
- Code signing / provisioning profile issue
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
#### F2. Android: "App crashes only on older devices (works on new)"
|
|
518
|
+
```
|
|
519
|
+
SEARCH:
|
|
520
|
+
1. Check minSdkVersion in android/app/build.gradle
|
|
521
|
+
2. Grep for API-level-specific code → missing compatibility check
|
|
522
|
+
COMMON:
|
|
523
|
+
- Using API not available on older Android (WebView, Notification channels)
|
|
524
|
+
- Missing desugaring for Java 8+ features
|
|
525
|
+
- Memory limit lower on older devices → OOM on large images
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
#### F3. Flutter: "RenderFlex overflowed by X pixels"
|
|
529
|
+
```
|
|
530
|
+
SEARCH: Find the widget from error → check its parent layout constraints
|
|
531
|
+
COMMON:
|
|
532
|
+
- Text too long in Row without Flexible/Expanded
|
|
533
|
+
- Image with fixed size in constrained container
|
|
534
|
+
- Column with unbounded children in ScrollView
|
|
535
|
+
FIX: Wrap in Flexible/Expanded OR add overflow: TextOverflow.ellipsis OR use SingleChildScrollView
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Issue Investigation Workflow
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
When user says "check this issue" / "investigate" / "look into":
|
|
544
|
+
|
|
545
|
+
STEP 1: READ the issue fully
|
|
546
|
+
→ Don't skim — read every word
|
|
547
|
+
→ Identify: what feature? what's expected? what's actual?
|
|
548
|
+
|
|
549
|
+
STEP 2: EXTRACT search terms
|
|
550
|
+
→ Feature name (e.g., "checkout", "profile", "notification")
|
|
551
|
+
→ Component/screen name (e.g., "CartScreen", "PaymentForm")
|
|
552
|
+
→ Error message (if any)
|
|
553
|
+
→ User action that triggers it (e.g., "tap submit", "pull to refresh")
|
|
554
|
+
|
|
555
|
+
STEP 3: SEARCH project
|
|
556
|
+
→ Grep feature name in src/
|
|
557
|
+
→ Glob for screen/component files
|
|
558
|
+
→ Read the relevant code (3-5 files max)
|
|
559
|
+
|
|
560
|
+
STEP 4: TRACE the flow
|
|
561
|
+
→ User action → event handler → state change → API call → response → UI update
|
|
562
|
+
→ Find where the flow breaks
|
|
563
|
+
|
|
564
|
+
STEP 5: REPORT (don't fix yet)
|
|
565
|
+
→ "I found [component] at [file:line]."
|
|
566
|
+
→ "Current behavior: [what code does]."
|
|
567
|
+
→ "The issue is likely: [root cause based on code read]."
|
|
568
|
+
→ "Confidence: [high/medium/low] based on [evidence]."
|
|
569
|
+
→ "Want me to fix it?"
|
|
570
|
+
|
|
571
|
+
STEP 6: FIX (only if user says yes)
|
|
572
|
+
→ Propose specific change with before/after
|
|
573
|
+
→ Check side effects
|
|
574
|
+
→ Verify types match
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Git-Aware Debugging (Check Recent Changes First)
|
|
580
|
+
|
|
581
|
+
```
|
|
582
|
+
⛔ BEFORE deep-diving into code, CHECK WHAT CHANGED RECENTLY.
|
|
583
|
+
⛔ 80%+ of bugs are caused by RECENT CHANGES — find them first.
|
|
584
|
+
|
|
585
|
+
PRE-CHECK: IS THIS A GIT PROJECT?
|
|
586
|
+
→ Run: git rev-parse --is-inside-work-tree 2>/dev/null
|
|
587
|
+
→ If command fails / returns false → NOT a git project → SKIP this section
|
|
588
|
+
→ If git repo but no commits (git log fails) → SKIP this section
|
|
589
|
+
→ If git repo with commits → proceed below
|
|
590
|
+
|
|
591
|
+
STEP 1: CHECK GIT HISTORY
|
|
592
|
+
→ git log --oneline -10 → recent commits
|
|
593
|
+
→ git diff HEAD~3 --name-only → files changed recently
|
|
594
|
+
→ git diff HEAD~3 -- [broken_file_path] → exact changes to broken file
|
|
595
|
+
→ git log --oneline --all -- [broken_file_path] → full file history
|
|
596
|
+
|
|
597
|
+
STEP 2: ANALYZE RECENT CHANGES
|
|
598
|
+
→ Did any recent commit touch the broken file/feature?
|
|
599
|
+
→ If YES → read the diff → the bug is likely IN that diff
|
|
600
|
+
→ If NO → bug is environmental/data-driven → proceed to Root Cause below
|
|
601
|
+
|
|
602
|
+
STEP 3: GIT BISECT (for "it used to work" bugs)
|
|
603
|
+
→ If user says "it worked before" / "it broke recently":
|
|
604
|
+
→ Find last known-good commit: git log --oneline -20
|
|
605
|
+
→ Compare: git diff [good_commit]..HEAD -- [affected_files]
|
|
606
|
+
→ The bug is in that diff
|
|
607
|
+
|
|
608
|
+
STEP 4: DEPENDENCY CHANGES
|
|
609
|
+
→ git diff HEAD~5 -- package.json pubspec.yaml build.gradle Podfile
|
|
610
|
+
→ Did any dependency version change? → common source of breakage
|
|
611
|
+
→ Check: lock file changes (yarn.lock, pubspec.lock, Podfile.lock)
|
|
612
|
+
|
|
613
|
+
SKIP GIT-AWARE DEBUGGING WHEN:
|
|
614
|
+
→ Project has NO git initialized (no .git/ folder)
|
|
615
|
+
→ git init done but zero commits yet
|
|
616
|
+
→ Brand new project (no meaningful history)
|
|
617
|
+
→ User explicitly says "this never worked" (not a regression)
|
|
618
|
+
→ Build error from fresh install (dependency/config issue)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## Root Cause Analysis Framework (5-Step Tracing)
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
⛔ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
|
|
627
|
+
⛔ NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE
|
|
628
|
+
|
|
629
|
+
STEP 1: OBSERVE — What exactly happens?
|
|
630
|
+
→ What user action triggers it? (tap, scroll, navigate, submit)
|
|
631
|
+
→ What is the EXACT error/behavior? (not paraphrased — exact)
|
|
632
|
+
→ When does it happen? (always / sometimes / after [action] / on [platform])
|
|
633
|
+
→ Classify reproduction:
|
|
634
|
+
ALWAYS → deterministic — trace step by step
|
|
635
|
+
SOMETIMES → race condition / timing — check async flow
|
|
636
|
+
ONLY on [device] → platform/hardware — check native layer
|
|
637
|
+
ONLY after [X] → state-dependent — trace state mutations
|
|
638
|
+
FIRST TIME ONLY → initialization — check setup/config
|
|
639
|
+
|
|
640
|
+
STEP 2: IMMEDIATE CAUSE — What code throws?
|
|
641
|
+
→ Find the EXACT file:line from error/stack trace
|
|
642
|
+
→ Read that file → what is the code doing at that line?
|
|
643
|
+
→ What variable/value is wrong? What should it be?
|
|
644
|
+
→ This is the IMMEDIATE cause (not root cause yet)
|
|
645
|
+
|
|
646
|
+
STEP 3: TRACE BACKWARD — What called this?
|
|
647
|
+
→ WHO calls this function? (Grep for callers in src/)
|
|
648
|
+
→ What data does the caller pass? Trace the data origin
|
|
649
|
+
→ Go one level up: who calls the CALLER?
|
|
650
|
+
→ Keep going until you find where the bad data ENTERED
|
|
651
|
+
|
|
652
|
+
STEP 4: FIND THE ROOT — Where does the chain break?
|
|
653
|
+
→ The root cause is where CORRECT data becomes INCORRECT
|
|
654
|
+
→ It's NOT the crash line — it's where the corruption starts
|
|
655
|
+
→ Common root causes:
|
|
656
|
+
- API response missing field (backend changed, frontend didn't)
|
|
657
|
+
- State not reset between screens (stale data leaks)
|
|
658
|
+
- Race condition: async A and B, B finishes first overwrites A
|
|
659
|
+
- Missing null check at data boundary (API → app)
|
|
660
|
+
- Wrong assumption about data shape (type says X, reality is Y)
|
|
661
|
+
→ VERIFY: does this root cause explain ALL symptoms?
|
|
662
|
+
- If yes → proceed to fix
|
|
663
|
+
- If no → your theory is wrong → go back to Step 3
|
|
664
|
+
|
|
665
|
+
STEP 5: FIX + DEFENSE IN DEPTH
|
|
666
|
+
→ Fix the root cause (not just the crash line)
|
|
667
|
+
→ Then add validation at EVERY layer data passes through:
|
|
668
|
+
Layer 1: API response validation (did backend send what we expect?)
|
|
669
|
+
Layer 2: State mutation guard (is the data valid before storing?)
|
|
670
|
+
Layer 3: Component prop check (does the component handle null/empty?)
|
|
671
|
+
Layer 4: Render guard (does UI degrade gracefully?)
|
|
672
|
+
→ Goal: make the bug STRUCTURALLY IMPOSSIBLE to recur
|
|
673
|
+
→ Not just "fix this one case" — prevent the entire CLASS of bug
|
|
674
|
+
→ Cite: "Root cause: [file:line] — [explanation]"
|
|
675
|
+
→ Cite: "Defense: added validation at [layers]"
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
## Single Hypothesis Protocol
|
|
681
|
+
|
|
682
|
+
```
|
|
683
|
+
⛔ NEVER stack multiple fixes at once
|
|
684
|
+
⛔ NEVER "fix it and also refactor nearby code"
|
|
685
|
+
|
|
686
|
+
PROTOCOL:
|
|
687
|
+
1. Form ONE hypothesis from root cause analysis
|
|
688
|
+
2. Make the SMALLEST possible change to test it
|
|
689
|
+
3. Verify: does it fix the bug?
|
|
690
|
+
→ YES → proceed to defense-in-depth
|
|
691
|
+
→ NO → REVERT the change → form NEW hypothesis
|
|
692
|
+
4. Never carry forward a failed fix
|
|
693
|
+
5. If 3 hypotheses fail → STOP and question architecture:
|
|
694
|
+
→ "I've tried 3 approaches and none fixed it.
|
|
695
|
+
This suggests the issue is architectural, not a simple bug.
|
|
696
|
+
Options: (1) [rethink approach], (2) [alternative pattern], (3) [ask user]"
|
|
697
|
+
|
|
698
|
+
WHY THIS MATTERS:
|
|
699
|
+
- Stacking fixes hides which one actually worked
|
|
700
|
+
- Multiple changes create new bugs while "fixing" old ones
|
|
701
|
+
- Clean revert = clean slate for next hypothesis
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## Red Flags — Anti-Rationalization Table
|
|
707
|
+
|
|
708
|
+
```
|
|
709
|
+
🚩 If you catch yourself saying/thinking these, STOP immediately:
|
|
710
|
+
|
|
711
|
+
WHAT YOU SAY WHAT IT MEANS WHAT TO DO
|
|
712
|
+
─────────────────────────────────────────────────────────────────────────
|
|
713
|
+
"Should work now" You didn't verify RUN IT. Read output.
|
|
714
|
+
"I'm confident this fixes it" Confidence ≠ evidence Show the evidence.
|
|
715
|
+
"I think the issue is..." You haven't traced the code Trace it. Cite file:line.
|
|
716
|
+
"Let me also clean up..." Scope creep during bug fix STOP. Fix bug only.
|
|
717
|
+
"Probably a race condition" Buzzword without proof PROVE with async trace.
|
|
718
|
+
"Try changing X to Y" You haven't read the file Read it FIRST.
|
|
719
|
+
"This is a known issue with X" You're pattern-matching Verify in THIS project.
|
|
720
|
+
Stacking 3+ changes at once You're guessing Revert. 1 change only.
|
|
721
|
+
Same fix attempted 3+ times Architecture problem STOP. Question approach.
|
|
722
|
+
"Works on my end" Didn't test other platform Test both. Or say untested.
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## Multi-Error Triage
|
|
728
|
+
|
|
729
|
+
```
|
|
730
|
+
When user has MULTIPLE errors (common during upgrades/migrations):
|
|
731
|
+
|
|
732
|
+
1. SORT by dependency:
|
|
733
|
+
→ Which error causes other errors? Fix that one FIRST
|
|
734
|
+
→ Build errors before runtime errors
|
|
735
|
+
→ Import errors before type errors
|
|
736
|
+
→ Config errors before code errors
|
|
737
|
+
|
|
738
|
+
2. FIND THE ROOT:
|
|
739
|
+
→ 5 errors might all come from 1 root cause
|
|
740
|
+
→ Example: wrong package version → type errors + build errors + runtime errors
|
|
741
|
+
→ Fix the version → all 5 errors disappear
|
|
742
|
+
|
|
743
|
+
3. FIX IN ORDER:
|
|
744
|
+
→ Root cause first
|
|
745
|
+
→ Then verify: did other errors disappear?
|
|
746
|
+
→ If yes → done
|
|
747
|
+
→ If no → fix remaining one by one
|
|
748
|
+
|
|
749
|
+
4. NEVER fix all errors at once without understanding relationships
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## Debugging Commands Cheat Sheet
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
REACT NATIVE:
|
|
758
|
+
Clear all caches: npx react-native start --reset-cache
|
|
759
|
+
Rebuild iOS: cd ios && rm -rf build && pod install && cd ..
|
|
760
|
+
Rebuild Android: cd android && ./gradlew clean && cd ..
|
|
761
|
+
Full nuke: rm -rf node_modules ios/Pods ios/build android/build && yarn && cd ios && pod install
|
|
762
|
+
Check linking: npx react-native config
|
|
763
|
+
Check bundle: npx react-native bundle --entry-file index.js --platform ios --dev false
|
|
764
|
+
|
|
765
|
+
FLUTTER:
|
|
766
|
+
Clear all: flutter clean && flutter pub get
|
|
767
|
+
Analyze: flutter analyze
|
|
768
|
+
Check deps: flutter pub outdated
|
|
769
|
+
Full rebuild: flutter clean && flutter pub get && cd ios && pod install && cd ..
|
|
770
|
+
|
|
771
|
+
EXPO:
|
|
772
|
+
Clear cache: npx expo start --clear
|
|
773
|
+
Check config: npx expo config
|
|
774
|
+
Prebuild: npx expo prebuild --clean
|
|
775
|
+
Doctor: npx expo-doctor
|
|
776
|
+
|
|
777
|
+
iOS:
|
|
778
|
+
Clean build: xcodebuild clean -workspace ios/App.xcworkspace -scheme App
|
|
779
|
+
Device logs: idevicesyslog (via libimobiledevice)
|
|
780
|
+
Simulator logs: xcrun simctl spawn booted log stream --level error
|
|
781
|
+
|
|
782
|
+
ANDROID:
|
|
783
|
+
Logcat filter: adb logcat *:E | grep "com.yourapp"
|
|
784
|
+
Clear app data: adb shell pm clear com.yourapp
|
|
785
|
+
Check crash: adb logcat -b crash
|
|
786
|
+
Screenshot: adb exec-out screencap -p > /tmp/screen.png
|
|
787
|
+
```
|