{"record":{"id":"fc52bbf121599ca8","repo":"lfnovo/open-notebook","slug":"failed-to-retry-episode","errorCode":null,"errorMessage":"Failed to retry episode","messagePattern":"Failed to retry episode","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"api/routers/podcasts.py","lineNumber":403,"sourceCode":"        await episode.delete()\n\n        # Submit a new job\n        job_id = await PodcastService.submit_generation_job(\n            episode_profile_name=ep_profile_name,\n            speaker_profile_name=sp_profile_name,\n            episode_name=episode_name,\n            content=content,\n        )\n\n        return {\"job_id\": job_id, \"message\": \"Retry submitted successfully\"}\n\n    except HTTPException:\n        raise\n    except OpenNotebookError:\n        raise\n    except Exception as e:\n        logger.error(f\"Error retrying podcast episode: {str(e)}\")\n        raise HTTPException(\n            status_code=500, detail=\"Failed to retry episode\"\n        )\n\n\n@router.delete(\"/podcasts/episodes/{episode_id}\")\nasync def delete_podcast_episode(episode_id: str):\n    \"\"\"Delete a podcast episode and its associated audio file\"\"\"\n    try:\n        # Get the episode first to check if it exists and get the audio file path\n        episode = await PodcastService.get_episode(episode_id)\n\n        # Delete the physical audio file if it exists\n        _delete_episode_audio(episode, episode_id)\n\n        # Delete the episode from the database\n        await episode.delete()\n\n        logger.info(f\"Deleted podcast episode: {episode_id}\")","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/api/routers/podcasts.py#L385-L421","documentation":"Generic 500 raised by the retry-podcast-episode endpoint when an unexpected (non-HTTP, non-OpenNotebook) exception escapes while retrying a failed podcast episode processing job. The original exception is logged server-side but not surfaced to the client.","triggerScenarios":"POST to /api/podcasts/episodes/{episode_id}/retry when the underlying retry logic raises anything outside HTTPException/OpenNotebookError — e.g. the episode record is missing/malformed, the job queue is unreachable, or a worker dependency throws.","commonSituations":"SurrealDB connection dropped, surreal-commands worker not running or stuck, episode row with corrupted JSON fields, version mismatch after upgrade between API and worker.","solutions":["Check API logs for the 'Error retrying podcast episode:' line to see the real exception","Verify the worker is running (make worker-start) and SurrealDB is up (make database)","Confirm the episode_id exists and its source record is intact","Re-ingest the podcast episode if its stored data is corrupted"],"exampleFix":"// before\nconst r = await fetch(`/api/podcasts/episodes/${id}/retry`);\n// after\nconst r = await fetch(`/api/podcasts/episodes/${id}/retry`);\nif (r.status === 500) {\n  console.error('Retry failed; check server logs for root cause');\n}","handlingStrategy":"try-catch","validationCode":"const ep = await fetch(`/api/podcasts/episodes/${id}`).then(r => r.ok ? r.json() : null);\nif (!ep) throw new Error('Episode not found');","typeGuard":null,"tryCatchPattern":"try {\n  await retryEpisode(id);\n} catch (e) {\n  if (e.status === 500) { /* inspect server log; surface generic retry message */ }\n  throw e;\n}","preventionTips":["Verify episode exists before retrying","Keep the worker running so retries have a consumer","Don't hammer retry on repeated 500s; inspect logs first"],"tags":["podcast","retry","http-500","worker-queue"],"backgroundTag":"internal-server-error","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}