datawhalechina/hello-agents · error · HTTPException

前端文件缺失,请检查 historical_review/web/static/

Error message

前端文件缺失,请检查 historical_review/web/static/

What it means

The FastAPI index route resolves historical_review/web/static/index.html via pathlib and raises HTTPException(500, '前端文件缺失...') when the file is not a regular file. The backend itself is healthy — this is a packaging/deployment error: the static frontend asset was not shipped alongside the Python package. Because it is raised on GET /, the whole UI appears broken even though /api/health works.

Source

Thrown at Co-creation-projects/meiguanxiHXX-historyReviewAgent/historical_review/web/app.py:67

class DebateResponse(BaseModel):
    ok: bool
    markdown: str | None = None
    error: str | None = None


def _api_key_error(req: DebateRequest) -> str | None:
    has_key = bool(req.api_key and req.api_key.strip())
    if not has_key and not (os.getenv("OPENROUTER_API_KEY") or os.getenv("LLM_API_KEY")):
        return "未配置 API Key:请在左侧填写 OpenRouter Key,或在服务器 .env 中设置 OPENROUTER_API_KEY。"
    return None


@app.get("/")
async def index_page() -> FileResponse:
    html = _STATIC / "index.html"
    if not html.is_file():
        raise HTTPException(status_code=500, detail="前端文件缺失,请检查 historical_review/web/static/")
    return FileResponse(html)


@app.get("/api/health")
async def health() -> dict[str, str]:
    return {"status": "ok"}


@app.post("/api/debate", response_model=DebateResponse)
async def run_debate(req: DebateRequest) -> DebateResponse:
    topic = req.topic.strip()
    if not topic:
        raise HTTPException(status_code=400, detail="议题不能为空")

    key_err = _api_key_error(req)
    if key_err:
        return DebateResponse(ok=False, error=key_err)

View on GitHub (pinned to 606a07d341)

Solutions

  1. Check the filesystem: ls historical_review/web/static/ — if index.html is absent, restore it from git (git checkout -- historical_review/web/static/) or run the frontend build that produces it.
  2. Run the server from the package root (where historical_review/ resolves) or anchor _STATIC to the module file: Path(__file__).parent / "static".
  3. For packaging, add package-data entries ("static/*" under historical_review.web) in pyproject/setup.py and build with the files included.
  4. In Docker, ensure COPY includes the static directory (COPY historical_review/ historical_review/).
  5. Sanity-check deployments with GET /api/health (ok) vs GET / (500) to distinguish backend vs asset problems.

Example fix

# before
_STATIC = Path("historical_review/web/static")  # cwd-dependent
# after
_STATIC = Path(__file__).resolve().parent / "static"  # anchored to app.py
html = _STATIC / "index.html"
if not html.is_file():
    raise HTTPException(status_code=500, detail=f"前端文件缺失: {html}")
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
_STATIC = Path(__file__).resolve().parent / "static"
html = _STATIC / "index.html"
if not html.is_file():
    raise SystemExit(f"static frontend missing at {html} — restore it or fix packaging")

Try / catch

from fastapi import HTTPException
try:
    return FileResponse(html)
except (FileNotFoundError, RuntimeError) as e:
    raise HTTPException(status_code=500, detail=f"frontend asset failure: {e}") from e

Prevention

When it happens

Trigger: Running the app from a different working directory so the relative _STATIC path resolves elsewhere; installing the package without package_data/include-package-data so static/ is excluded from the wheel; a Docker image that copies *.py but not historical_review/web/static/; a repo checkout where the static folder was gitignored or deleted.

Common situations: pip install from a wheel/sdist that forgot static assets; docker build with COPY src/ only; running uvicorn with an odd --app-dir; frontend build step never ran (if index.html is generated rather than committed).

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/5e4c55ae1a36a6e0. Report an issue: GitHub.