{"record":{"id":"95f1669c3a402486","repo":"Zackriya-Solutions/meetily","slug":"meeting-metadata-not-found","errorCode":null,"errorMessage":"Meeting metadata not found","messagePattern":"Meeting metadata not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/hooks/useTranscriptRecovery.ts","lineNumber":116,"sourceCode":"      // Sort by sequence ID\n      transcripts.sort((a, b) => (a.sequenceId || 0) - (b.sequenceId || 0));\n      return transcripts;\n    } catch (error) {\n      console.error('Failed to load meeting transcripts:', error);\n      return [];\n    }\n  }, []);\n\n  /**\n   * Recover a meeting from IndexedDB\n   */\n  const recoverMeeting = useCallback(async (meetingId: string): Promise<{ success: boolean; audioRecoveryStatus?: AudioRecoveryStatus | null; meetingId?: string }> => {\n    setIsRecovering(true);\n    try {\n      // 1. Load meeting metadata\n      const metadata = await indexedDBService.getMeetingMetadata(meetingId);\n      if (!metadata) {\n        throw new Error('Meeting metadata not found');\n      }\n\n      // 2. Load all transcripts\n      const transcripts = await loadMeetingTranscripts(meetingId);\n      if (transcripts.length === 0) {\n        throw new Error('No transcripts found for this meeting');\n      }\n\n      // 3. Check for folder path\n      let folderPath = metadata.folderPath;\n\n\n      if (!folderPath) {\n        // Try to get from backend (might exist if only app crashed, not system)\n        try {\n          folderPath = await invoke<string>('get_meeting_folder_path');\n        } catch (error) {\n          folderPath = undefined;","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/Zackriya-Solutions/meetily/blob/0281737d87d26352fb0adc78c8c0975f691b23d1/frontend/src/hooks/useTranscriptRecovery.ts#L98-L134","documentation":"recoverMeeting first loads meeting metadata from IndexedDB via indexedDBService.getMeetingMetadata(meetingId) and throws when it resolves to nothing. IndexedDB in the WebView is the crash-recovery store, so this means no metadata record exists for that ID: it was never written, was removed by storage eviction or a user clearing app data, or the store/schema name changed after an upgrade. Everything downstream in recovery (transcripts, folderPath, audio) depends on metadata, so the flow aborts immediately.","triggerScenarios":"recoverMeeting called with an ID that is not in the metadata object store: recovery list UI holds an ID from a previous session, WebView storage was evicted under disk pressure, IndexedDB schema version bumped and old stores were orphaned, or the crash happened before metadata was ever persisted.","commonSituations":"User cleared browsing/site data for the WebView, disk-pressure eviction on the IndexedDB volume, an app upgrade that recreated the DB without migrating records, or recovery offered for a meeting recorded before the persistence feature existed.","solutions":["Verify the meetingId still exists: enumerate the metadata store and only offer recovery for IDs it returns.","Check whether WebView site data was cleared or evicted — if so, IndexedDB recovery is impossible; fall back to the on-disk audio folder (folderPath from the meetings list).","If the IndexedDB schema version changed, verify the store name/keyPath in indexedDBService matches what the records were written under, and add migrations.","Wrap recoverMeeting in try-catch and degrade to audio-only recovery rather than failing the whole recovery dialog."],"exampleFix":"// before\nconst metadata = await indexedDBService.getMeetingMetadata(meetingId);\nif (!metadata) {\n  throw new Error('Meeting metadata not found');\n}\n\n// after\nconst metadata = await indexedDBService.getMeetingMetadata(meetingId);\nif (!metadata) {\n  toast.error('Meeting no longer in local storage', {\n    description: 'It may have been cleared. Recover from the saved audio file instead.',\n  });\n  return { success: false };\n}","handlingStrategy":"validation","validationCode":"const recoverableIds = new Set(\n  (await indexedDBService.getAllMeetingMetadata()).map(m => m.meetingId)\n);\nif (!recoverableIds.has(meetingId)) {\n  toast.error('This meeting is no longer in local storage.');\n  return;\n}","typeGuard":"function isMeetingMetadata(m: unknown): m is MeetingMetadata {\n  const c = m as MeetingMetadata;\n  return !!c && typeof c.meetingId === 'string' && !!c.createdAt;\n}","tryCatchPattern":"try {\n  await recoverMeeting(meetingId);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Meeting metadata not found') {\n    // offer disk-based audio recovery from the meetings list instead of failing the dialog\n  }\n}","preventionTips":["Persist meeting metadata to IndexedDB at recording start, not stop, so crashes always leave a record.","Treat on-disk audio folders as the source of truth; IndexedDB is only an accelerator.","Version the IndexedDB schema and migrate stores on upgrade so old IDs keep resolving."],"tags":["indexeddb","recovery","metadata","crash-recovery"],"backgroundTag":"indexeddb-record-not-found","analyzedSha":"0281737d87d26352fb0adc78c8c0975f691b23d1","analyzedAt":"2026-08-16T20:57:52.567Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}