{"record":{"id":"8c3c6ca73898192f","repo":"jamiepine/voicebox","slug":"story-or-generation-not-found","errorCode":null,"errorMessage":"Story or generation not found","messagePattern":"Story or generation not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"backend/routes/stories.py","lineNumber":81,"sourceCode":"    db: Session = Depends(get_db),\n):\n    \"\"\"Delete a story.\"\"\"\n    success = await stories.delete_story(story_id, db)\n    if not success:\n        raise HTTPException(status_code=404, detail=\"Story not found\")\n    return {\"message\": \"Story deleted successfully\"}\n\n\n@router.post(\"/stories/{story_id}/items\", response_model=models.StoryItemDetail)\nasync def add_story_item(\n    story_id: str,\n    data: models.StoryItemCreate,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Add a generation to a story.\"\"\"\n    item = await stories.add_item_to_story(story_id, data, db)\n    if not item:\n        raise HTTPException(status_code=404, detail=\"Story or generation not found\")\n    return item\n\n\n@router.delete(\"/stories/{story_id}/items/{item_id}\")\nasync def remove_story_item(\n    story_id: str,\n    item_id: str,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Remove a story item from a story.\"\"\"\n    success = await stories.remove_item_from_story(story_id, item_id, db)\n    if not success:\n        raise HTTPException(status_code=404, detail=\"Story item not found\")\n    return {\"message\": \"Item removed successfully\"}\n\n\n@router.put(\"/stories/{story_id}/items/times\")\nasync def update_story_item_times(","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/stories.py#L63-L99","documentation":"Returned by POST /stories/{story_id}/items when stories.add_item_to_story returns falsy (HTTP 404). The single guard covers two distinct miss conditions: the story_id does not exist, or the generation referenced in StoryItemCreate does not exist. The message combines both because the service returns one falsy signal for either.","triggerScenarios":"Adding a generation to a non-existent story, or referencing a generation_id that does not exist (wrong id, deleted, or belongs to another scope). The handler cannot distinguish which from the return value alone.","commonSituations":"Generation was deleted between creation and adding to a story. Story_id from a stale list. Generation_id copied from a different environment.","solutions":["Verify both story_id (GET /stories/{story_id}) and the referenced generation exist before adding.","Refresh the generations list to obtain a current generation_id.","If the message is ambiguous in the UI, pre-check both entities to disambiguate.","Avoid holding generation ids long-term; re-fetch before use."],"exampleFix":"// before\nawait api.post(`/stories/${sid}/items`, { generation_id: gen });\n\n// after\nif (!await storyExists(sid) || !await generationExists(gen))\n  throw new NotFoundError('story or generation');\nawait api.post(`/stories/${sid}/items`, { generation_id: gen });","handlingStrategy":"validation","validationCode":"async function canAddItem(storyId, generationId) {\n  const [s, g] = await Promise.all([\n    fetch(`/api/stories/${encodeURIComponent(storyId)}`),\n    fetch(`/api/generations/${encodeURIComponent(generationId)}`),\n  ]);\n  return s.ok && g.ok;\n}","typeGuard":"function isItemCreate(v) { return !!v && typeof v.generation_id === 'string'; }","tryCatchPattern":"try { await api.post(`/stories/${sid}/items`, payload); }\ncatch (e) { if (e.response?.status === 404) disambiguateStoryOrGeneration(sid, payload.generation_id); throw e; }","preventionTips":["Verify both story and generation exist before adding.","Re-fetch generation ids before use; don't hold them long-term.","Pre-check both entities to disambiguate the combined 404 message."],"tags":["fastapi","http","not-found","stories","generations","rest"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}