datawhalechina/hello-agents · warning · HTTPException
Chapter not found
Error message
Chapter not found
What it means
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.
Source
Thrown at Co-creation-projects/lgs-only-NovelGenerator/src/app.py:225
break
return {"generated_chapters": generated_chapters}
@app.get("/chapter/{title}/{novel_id}/{note_id}")
def get_chapter(title: str, novel_id: str, note_id: str):
path = os.path.join("./outputs", f"{title}-{novel_id}", "chapters", f"{note_id}.md")
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
content = f.read()
# Remove frontmatter
if content.startswith("---"):
parts = content.split("---", 2)
if len(parts) >= 3:
content = parts[2].strip()
return {"content": content}
raise HTTPException(status_code=404, detail="Chapter not found")
@app.put("/chapter/update")
def update_chapter(req: ChapterUpdateRequest):
update_kwargs = {}
if req.content is not None:
update_kwargs["content"] = req.content
if req.chapter_title is not None:
update_kwargs["title"] = req.chapter_title
if req.summary is not None:
update_kwargs["summary"] = req.summary
if req.next_chapter_prediction is not None:
update_kwargs["next_chapter_prediction"] = req.next_chapter_prediction
chapter_agent.update_chapter(req.novel_id, req.note_id, novel_title=req.title, **update_kwargs)
# Update mapping if title/summary changed
mapping_update = {}
if req.chapter_title:View on GitHub (pinned to 606a07d341)
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.
Example fix
# before
path = os.path.join("./outputs", f"{title}-{novel_id}", "chapters", f"{note_id}.md")
# after
OUTPUTS_DIR = Path(__file__).resolve().parent.parent / "outputs"
path = OUTPUTS_DIR / f"{title}-{novel_id}" / "chapters" / f"{note_id}.md"
if not path.is_file():
raise HTTPException(status_code=404, detail=f"Chapter not found: {note_id} (looked in {path}") Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def chapter_path(title: str, novel_id: str, note_id: str, base: Path) -> Path:
return base / f"{title}-{novel_id}" / "chapters" / f"{note_id}.md"
p = chapter_path(title, novel_id, note_id, Path("./outputs").resolve())
if not p.is_file():
available = sorted(x.name for x in p.parent.glob("*.md")) if p.parent.is_dir() else []
# surface available chapters to the caller instead of a bare 404
raise FileNotFoundError(f"{note_id} not found; existing: {available}") Try / catch
from fastapi import HTTPException
try:
...read chapter...
except FileNotFoundError as e:
raise HTTPException(status_code=404, detail=str(e)) from e Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/2dd9e890fcacbd81.
Report an issue: GitHub.