{"record":{"id":"2dd9e890fcacbd81","repo":"datawhalechina/hello-agents","slug":"chapter-not-found","errorCode":null,"errorMessage":"Chapter not found","messagePattern":"Chapter not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"Co-creation-projects/lgs-only-NovelGenerator/src/app.py","lineNumber":225,"sourceCode":"            break\n        \n    return {\"generated_chapters\": generated_chapters}\n\n@app.get(\"/chapter/{title}/{novel_id}/{note_id}\")\ndef get_chapter(title: str, novel_id: str, note_id: str):\n    path = os.path.join(\"./outputs\", f\"{title}-{novel_id}\", \"chapters\", f\"{note_id}.md\")\n    if os.path.exists(path):\n        with open(path, \"r\", encoding=\"utf-8\") as f:\n            content = f.read()\n        \n        # Remove frontmatter\n        if content.startswith(\"---\"):\n            parts = content.split(\"---\", 2)\n            if len(parts) >= 3:\n                content = parts[2].strip()\n        \n        return {\"content\": content}\n    raise HTTPException(status_code=404, detail=\"Chapter not found\")\n\n@app.put(\"/chapter/update\")\ndef update_chapter(req: ChapterUpdateRequest):\n    update_kwargs = {}\n    if req.content is not None:\n        update_kwargs[\"content\"] = req.content\n    if req.chapter_title is not None:\n        update_kwargs[\"title\"] = req.chapter_title\n    if req.summary is not None:\n        update_kwargs[\"summary\"] = req.summary\n    if req.next_chapter_prediction is not None:\n        update_kwargs[\"next_chapter_prediction\"] = req.next_chapter_prediction\n        \n    chapter_agent.update_chapter(req.novel_id, req.note_id, novel_title=req.title, **update_kwargs)\n    \n    # Update mapping if title/summary changed\n    mapping_update = {}\n    if req.chapter_title:","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/lgs-only-NovelGenerator/src/app.py#L207-L243","documentation":"The FastAPI chapter-read endpoint builds the path ./outputs/{title}-{novel_id}/chapters/{note_id}.md and raises HTTPException(404, 'Chapter not found') when os.path.exists(path) is false. It is a plain missing-file 404: the chapter markdown for that note_id was never written, was written under a different title/novel_id combination, or the process's working directory is not where ./outputs lives. Note the frontend-stripping logic (splitting on '---') only runs after existence is confirmed, so it is never the cause.","triggerScenarios":"GET/POST to the chapter endpoint with a note_id that was never generated (e.g. chapter generation crashed before saving); title or novel_id mismatch with the directory name used at generation time (title changed between runs); starting uvicorn from a different cwd so './outputs' resolves elsewhere; requesting a chapter from another novel whose folder uses a different {title}-{novel_id} prefix.","commonSituations":"Frontend holds a stale note_id after the outputs directory was wiped; running the app from repo root during dev but from src/ in production, changing the relative path; special characters or slashes in title producing a different folder name; generation pipeline partially failed so later chapters were never created.","solutions":["List the actual folder: ls \"./outputs/{title}-{novel_id}/chapters/\" to see which note_id files exist and correct the request.","If files exist, fix the working directory: start uvicorn from the project root or make the path absolute (e.g. base dir from a setting/env) instead of './outputs'.","Verify title/novel_id exactly match the generation-time folder name (encoding, punctuation, full-width characters).","Regenerate the missing chapter if the generation run failed before writing it.","Return a friendlier 404 body including the resolved path so debugging does not require server access."],"exampleFix":"# before\npath = os.path.join(\"./outputs\", f\"{title}-{novel_id}\", \"chapters\", f\"{note_id}.md\")\n# after\nOUTPUTS_DIR = Path(__file__).resolve().parent.parent / \"outputs\"\npath = OUTPUTS_DIR / f\"{title}-{novel_id}\" / \"chapters\" / f\"{note_id}.md\"\nif not path.is_file():\n    raise HTTPException(status_code=404, detail=f\"Chapter not found: {note_id} (looked in {path}\")","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef chapter_path(title: str, novel_id: str, note_id: str, base: Path) -> Path:\n    return base / f\"{title}-{novel_id}\" / \"chapters\" / f\"{note_id}.md\"\n\np = chapter_path(title, novel_id, note_id, Path(\"./outputs\").resolve())\nif not p.is_file():\n    available = sorted(x.name for x in p.parent.glob(\"*.md\")) if p.parent.is_dir() else []\n    # surface available chapters to the caller instead of a bare 404\n    raise FileNotFoundError(f\"{note_id} not found; existing: {available}\")","typeGuard":null,"tryCatchPattern":"from fastapi import HTTPException\ntry:\n    ...read chapter...\nexcept FileNotFoundError as e:\n    raise HTTPException(status_code=404, detail=str(e)) from e","preventionTips":["Anchor output directories to absolute paths derived from __file__","Verify note_id came from a completed generation before exposing a read URL","Include the resolved path and available files in 404 details"],"tags":["http-404","file-not-found","fastapi","path-resolution","python"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}