Fix: Silent Production Errors (Batch)
Branch: fix/silent-errors-batch
Date: 2026-03-18
Impact: 9 error categories that crashed silently without being reported to Supabase
Overview
A batch of production errors was identified that were not being caught or reported to the app_issues table in Supabase. These spanned several services and were grouped into 9 categories. All have been fixed and all catch blocks now properly report via IssueReportService.
Error 1 — Squad fetching (squad_service.dart)
Root cause: getMySquad() ordered by .order('joined_at') — this column does not exist in squad_members. PostgREST threw a PostgrestException which was swallowed silently.
Fix: Changed to .order('created_at', ascending: false).
Error 2 — PostgrestException in notifications (notification_repository.dart)
Root cause: getNotifications() had no try-catch at all. Any Supabase error propagated uncaught into the caller. Additionally, it fetched all notifications (missing user_id filter).
Fix:
- Wrapped all three methods (
getNotifications,markAsRead,markAllAsRead) in try-catch. - Added
.eq('user_id', userId)filter togetNotifications(). - All catch blocks now report to
IssueReportService.
Error 3 — Exception from image resource service (storage_service.dart)
Root cause: uploadProfileImage() accepted a dart:io File, which throws UnsupportedError on Flutter Web. The MIME type was also derived from file.path (a blob: URL on web), yielding a garbage extension.
Fix:
- Changed parameter from
FiletoXFile(fromimage_picker). - Uses
file.namefor extension extraction (real filename, not blob URL). - Uses
file.readAsBytes()instead ofFile.readAsBytes()— cross-platform. - Switched from
.upload()to.uploadBinary(). - Normalizes
jpg→image/jpegMIME type.
Error 4 — UnimplementedError in image picker (registration_wizard_notifier.dart)
Root cause: pickImage() called ImageCropper().cropImage() and File(croppedFile.path) on Flutter Web — both unsupported, throwing UnimplementedError.
Fix:
- Added
kIsWebguard: skipsImageCropperentirely on web. - On native, wrapped
ImageCroppercall in try-catch with fallback to originalXFile. - Changed
File(croppedFile.path)→XFile(croppedFile.path)to match updatedStorageServicesignature.
Error 5 — PostgrestException in social list (social_list_screen.dart)
Root cause: _fetchFollowers() and _fetchFollowing() used PostgREST FK hint syntax (profiles!friendships_user_id_fkey(...)). If the FK constraint name doesn’t exactly match the schema, PostgREST returns a PGRST200 error.
Fix: Replaced with explicit two-step queries — first fetch IDs from friendships, then fetch matching profiles with .inFilter('id', ids). More verbose but reliable.
Error 6 — PostgrestException: count cast (crew_service.dart)
Root cause: getActiveCrews() cast the PostgREST count result directly: members[0]['count'] as int. PostgREST may return count as a String depending on version/configuration, causing a type cast exception.
Fix: Changed to int.tryParse(members[0]['count'].toString()) ?? 0 — handles both String and int safely.
Error 7 — PostgrestException in squad search (search_screen.dart)
Root cause: The search query on the squads table used .ilike('title', ...) — but the squads table has no title column. The actual searchable column is vibe_tag.
Fix: Changed to .ilike('vibe_tag', '%$query%').
Error 8 — Always-silent errors in notification provider (notification_provider.dart)
Root cause: NotificationNotifier used print() for all error output — errors were logged to console only and never reported. Also missing user_id filter in loadNotifications() and markAllAsRead().
Fix:
- Replaced all
print()calls withIssueReportService().reportIssue(...). - Added
user_idfilter toloadNotifications()andmarkAllAsRead(). - Added early return if user is not authenticated.
Error 9 — PostgrestException in crew fetching (crew_service.dart)
Root cause: getMyCrew() ordered crew_members by .order('joined_at') — same as Error 1 pattern. joined_at does not exist; the correct column is created_at.
Fix: Changed to .order('created_at', ascending: false).
Files Changed
| File | Changes |
|---|---|
lib/features/social/services/squad_service.dart | joined_at → created_at, added IssueReportService |
lib/shared/repositories/notification_repository.dart | try-catch for all methods, user_id filter |
lib/core/services/storage_service.dart | File → XFile, readAsBytes(), extension from file.name |
lib/features/auth/notifiers/registration_wizard_notifier.dart | kIsWeb guard, XFile instead of File |
lib/features/profile/screens/social_list_screen.dart | FK hints removed, two-step explicit queries |
lib/features/social/services/crew_service.dart | Safe count cast, joined_at → created_at, IssueReportService |
lib/features/search/screens/search_screen.dart | title → vibe_tag for squads search |
lib/shared/providers/notification_provider.dart | print() → IssueReportService, user_id filter |