{"record":{"id":"0410f525c880c4e5","repo":"Zackriya-Solutions/meetily","slug":"no-transcript-text-available-please-add-some-text","errorCode":null,"errorMessage":"No transcript text available. Please add some text first.","messagePattern":"No transcript text available\\. Please add some text first\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/src/hooks/meeting-details/useSummaryGeneration.ts","lineNumber":116,"sourceCode":"\n  // Unified summary processing logic\n  const processSummary = useCallback(async ({\n    transcriptText,\n    transcriptTexts,\n    customPrompt = '',\n    isRegeneration = false,\n  }: {\n    transcriptText: string;\n    transcriptTexts?: string[];\n    customPrompt?: string;\n    isRegeneration?: boolean;\n  }) => {\n    setSummaryStatus(isRegeneration ? 'regenerating' : 'processing');\n    setSummaryError(null);\n\n    try {\n      if (!transcriptText.trim()) {\n        throw new Error('No transcript text available. Please add some text first.');\n      }\n\n      console.log('Processing transcript with template:', selectedTemplate);\n\n      // Calculate time since recording\n      const timeSinceRecording = (Date.now() - new Date(meeting.created_at).getTime()) / 60000; // minutes\n\n      // Track summary generation started\n      await Analytics.trackSummaryGenerationStarted(\n        modelConfig.provider,\n        modelConfig.model,\n        transcriptText.length,\n        timeSinceRecording\n      );\n\n      // Track custom prompt usage if present\n      if (customPrompt.trim().length > 0) {\n        await Analytics.trackCustomPromptUsed(customPrompt.trim().length);","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/Zackriya-Solutions/meetily/blob/0281737d87d26352fb0adc78c8c0975f691b23d1/frontend/src/hooks/meeting-details/useSummaryGeneration.ts#L98-L134","documentation":"Thrown by the summary-generation hook as a deliberate precondition check: generateSummary refuses to run when transcriptText.trim() is empty, before any LLM call or Analytics.trackSummaryGenerationStarted fires. Note the hook has already set summaryStatus to 'processing'/'regenerating' by the time this throws, so the catch path must reset that status. In practice the transcript is empty because Whisper/VAD produced no segments (or segments have not loaded), not because of a bug in this guard.","triggerScenarios":"Calling generateSummary (or the Regenerate action) when: (1) the meeting was stopped seconds after starting and no speech was detected; (2) transcriptTexts was used elsewhere but the joined transcriptText passed here is empty/whitespace; (3) an old meeting's transcripts failed to load from the database and the UI still enabled the button; (4) VAD filtered out all audio so the transcript-update events never carried text.","commonSituations":"Very short or silent recordings, mic permission denied so Whisper receives nothing, opening a meeting whose transcript rows were not persisted, or a race where the user clicks Generate before the first transcript segment arrives.","solutions":["Check that transcript segments actually exist before enabling the Generate/Regenerate button (e.g. transcripts.some(t => t.text.trim().length > 0)).","When regenerating from transcriptTexts, join segments with newlines and trim the result before passing it as transcriptText.","If transcripts stay empty during live recording, debug the audio pipeline: run with RUST_LOG=app_lib::audio=debug and verify transcript-update events and the VAD detection rate.","As a workaround, paste manual transcript text into the meeting and retry."],"exampleFix":"// before\nawait generateSummary({ transcriptText, isRegeneration: true });\n\n// after\nif (!transcriptText.trim()) {\n  setSummaryError('Transcript is empty — record or add text before generating a summary.');\n  setSummaryStatus('idle'); // reset the 'processing' state the hook already set\n  return;\n}\nawait generateSummary({ transcriptText, isRegeneration: true });","handlingStrategy":"validation","validationCode":"const hasText = transcriptTexts?.length\n  ? transcriptTexts.some(t => t.trim().length > 0)\n  : !!transcriptText?.trim();\nif (!hasText) {\n  toast.info('Nothing to summarize yet — the transcript is empty.');\n  return;\n}","typeGuard":"function hasTranscriptText(text: string | null | undefined): text is string {\n  return typeof text === 'string' && text.trim().length > 0;\n}","tryCatchPattern":"try {\n  await generateSummary({ transcriptText });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('No transcript text')) {\n    setSummaryStatus('idle'); // reset the 'processing' state the hook set before throwing\n    return;\n  }\n  throw e;\n}","preventionTips":["Disable the Generate/Regenerate button until at least one transcript segment exists.","After stopping a recording, assert transcript-update events produced non-empty segments before showing summary UI.","When joining segments into transcriptText, trim the result and prompt the user when it's empty."],"tags":["transcript","summary","validation","react-hook"],"backgroundTag":"empty-input-validation","analyzedSha":"0281737d87d26352fb0adc78c8c0975f691b23d1","analyzedAt":"2026-08-16T20:57:52.567Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}