{"record":{"id":"6d73cd87b0ade7f3","repo":"lfnovo/open-notebook","slug":"failed-to-create-episode-profile","errorCode":null,"errorMessage":"Failed to create episode profile","messagePattern":"Failed to create episode profile","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"api/routers/episode_profiles.py","lineNumber":173,"sourceCode":"            speaker_config=str(speaker.id),\n            outline_llm=profile_data.outline_llm,\n            transcript_llm=profile_data.transcript_llm,\n            language=profile_data.language,\n            default_briefing=profile_data.default_briefing,\n            num_segments=profile_data.num_segments,\n            max_tokens=profile_data.max_tokens,\n        )\n\n        await profile.save()\n        return _profile_to_response(profile, speaker.name)\n\n    except HTTPException:\n        raise\n    except OpenNotebookError:\n        raise\n    except Exception as e:\n        logger.error(f\"Failed to create episode profile: {e}\")\n        raise HTTPException(\n            status_code=500, detail=\"Failed to create episode profile\"\n        )\n\n\n@router.put(\"/episode-profiles/{profile_id}\", response_model=EpisodeProfileResponse)\nasync def update_episode_profile(profile_id: str, profile_data: EpisodeProfileCreate):\n    \"\"\"Update an existing episode profile\"\"\"\n    try:\n        profile = await EpisodeProfile.get(profile_id)\n\n        if not profile:\n            raise HTTPException(\n                status_code=404, detail=f\"Episode profile '{profile_id}' not found\"\n            )\n\n        update_data = profile_data.model_dump(exclude_unset=True)\n        speaker_name: Optional[str] = None\n        if \"speaker_config\" in update_data:","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/api/routers/episode_profiles.py#L155-L191","documentation":"Generic 500 raised when creating an episode profile fails unexpectedly — after the HTTP/NotFound/InvalidInput/OpenNotebookError paths were excluded. Typically a save or uniqueness failure at the DB layer.","triggerScenarios":"POST /api/episode-profiles failing during db.save() — DB unreachable, unique-name constraint violation surfaced as a raw driver error, or invalid model state.","commonSituations":"Duplicate profile name that isn't caught by the app-level check (race between two concurrent creates), DB schema mismatch after migration, DB outage.","solutions":["Check logs for the exact exception during create","If it's a duplicate name, GET the list and pick a unique name","Verify SurrealDB health and retry","For race conditions on unique names, retry with a suffixed name or add app-level uniqueness enforcement"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"existing = {p[\"name\"] for p in (await client.get(\"/api/episode-profiles\")).json()}\nif payload[\"name\"] in existing:\n    payload[\"name\"] = f\"{payload['name']} ({uuid.uuid4().hex[:4]})\"","typeGuard":null,"tryCatchPattern":"try:\n    resp = await client.post(\"/api/episode-profiles\", json=payload)\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code >= 500:\n        # check logs; likely name collision or DB issue — retry with unique name\n        payload[\"name\"] += f\" {uuid.uuid4().hex[:4]}\"\n        resp = await client.post(\"/api/episode-profiles\", json=payload)\n    raise","preventionTips":["Choose unique names client-side before submitting","Disable submit buttons to prevent double-create races","Verify speaker_config validity before create to reduce 500 paths"],"tags":["http-500","internal-error","episode-profile","create","unique-constraint"],"backgroundTag":"internal-server-error","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}