abi/screenshot-to-code · warning · HTTPException
Invalid report filename
Error message
Invalid report filename
What it means
400 raised by GET /prompt-reports/content in backend/routes/prompt_reports.py:154 when the filename query parameter does not match PROMPT_REPORT_FILENAME_PATTERN — the strict schema ^prompt_report_<YYYYMMDD>_<HHMMSS>_<8 hex session>_t<turn>_<provider>_<model>.json$ (backend/fs_logging/prompt_reports.py:29). This is also a path-traversal guard: any ../ or unexpected character fails the match before the file is opened.
Source
Thrown at backend/routes/prompt_reports.py:154
reports.sort(key=lambda report: (report.created_at, report.turn), reverse=True)
total_size_bytes = (
_directory_size_bytes(run_logs_directory)
if os.path.isdir(run_logs_directory)
else 0
)
return PromptReportListResponse(
reports=reports,
total_size_bytes=total_size_bytes,
reports_directory=reports_directory,
)
@router.get("/prompt-reports/content")
async def get_prompt_report_content(filename: str) -> Any:
if PROMPT_REPORT_FILENAME_PATTERN.match(filename) is None:
raise HTTPException(status_code=400, detail="Invalid report filename")
filepath = os.path.join(get_prompt_reports_directory(), filename)
if not os.path.isfile(filepath):
raise HTTPException(status_code=404, detail="Report not found")
try:
with open(filepath, "r", encoding="utf-8") as f:
return json.load(f)
except (OSError, json.JSONDecodeError) as e:
raise HTTPException(status_code=500, detail=f"Failed to read report: {e}")
@router.post("/prompt-reports/prune", response_model=PrunePromptReportsResponse)
async def prune_prompt_reports(
request: PrunePromptReportsRequest,
) -> PrunePromptReportsResponse:
if request.max_age_days < 1:
raise HTTPException(status_code=400, detail="max_age_days must be >= 1")View on GitHub (pinned to d026163f58)
Solutions
- Use the exact filename returned by the GET /prompt-reports listing endpoint (its summaries come from the same pattern).
- URL-encode the filename when building the query string.
- If the file legitimately exists but is named differently, it was not written by this logger — check the naming pattern before renaming to match.
Example fix
// before
const url = `/prompt-reports/content?filename=${reportDate}_${reportTime}.json`;
// after
const url = `/prompt-reports/content?filename=${encodeURIComponent(report.filename)}`; // exact value from GET /prompt-reports Defensive patterns
Strategy: validation
Validate before calling
const FILENAME_RE = /^prompt_report_\d{8}_\d{6}_[0-9a-f]{8}_t\d+_[a-z0-9]+_[A-Za-z0-9.-]+\.json$/;
if (!FILENAME_RE.test(filename)) throw new Error(`Invalid report filename: ${filename}`); Type guard
function isPromptReportFilename(name: string): boolean {
return /^prompt_report_\d{8}_\d{6}_[0-9a-f]{8}_t\d+_[a-z0-9]+_[A-Za-z0-9.-]+\.json$/.test(name);
} Try / catch
const res = await fetch(`/prompt-reports/content?filename=${encodeURIComponent(filename)}`);
if (res.status === 400) { /* do not retry: name is malformed; re-fetch from list */ }
if (res.status === 404) { /* report was pruned; refresh the list */ } Prevention
- Only use filenames verbatim from the GET /prompt-reports listing.
- URL-encode filenames; never build them from date/model parts client-side.
When it happens
Trigger: Calling the endpoint with a hand-built filename, a filename copied with whitespace/case changes, a traversal attempt like ../../backend/.env, or a report written by an older naming scheme.
Common situations: Clients constructing the URL from report metadata fields instead of using the exact filename from the reports list; reports migrated from another machine with different naming; security scanners probing with traversal strings.
Related errors
- Invalid run id
- Invalid eval set name: {set_name!r}
- Invalid asset path
- max_age_days must be >= 1
- Design system name is required
AI-assisted analysis of abi/screenshot-to-code@d026163f58 (2026-08-14).
Data as JSON: /api/errors/0f5cbf3650074bd6.
Report an issue: GitHub.