Skip to content

Temporary PDF file created with delete=False is never cleaned up #17

@Devguru-codes

Description

@Devguru-codes

Location

apps/backend/app/routers/reports.py:129-132

Description

The download_pdf endpoint creates a temporary PDF file with tempfile.NamedTemporaryFile(delete=False) and returns it via FileResponse. However, the file is never deleted after the response is sent. On a long-running server, each PDF generation leaks a temp file, accumulating disk usage over time.

Reproduction (on main branch)

Buggy Code

# apps/backend/app/routers/reports.py:129-132
@report_router.post("/report-pdf")
async def download_pdf(report_data: dict):
    html_content = render_report_html(report_data["report_data"])
    with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
        HTML(string=html_content).write_pdf(tmp.name)
        tmp_path = tmp.name
    return FileResponse(tmp_path, media_type="application/pdf", filename="report.pdf")
    # ^^^ tmp_path is NEVER deleted — file leaks on every request

Reproduction Script (reproduce_bug.py)

import inspect, os

reports_path = "apps/backend/app/routers/reports.py"
with open(reports_path, 'r') as f:
    source = f.read()

func_start = source.find("async def download_pdf")
func_source = source[func_start:]

# Check 1: delete=False used?
print("delete=False:", "delete=False" in func_source)  # True

# Check 2: Any cleanup (os.remove, os.unlink, BackgroundTask)?
has_cleanup = "os.remove" in func_source or "os.unlink" in func_source or "background" in func_source.lower()
print("Cleanup exists:", has_cleanup)  # False — BUG!

Automated Results on main branch

# Test Status Detail
1 Temp PDF created with delete=False FAIL delete=False used, file persists after response
2 Temp PDF is cleaned up after response FAIL No os.remove/os.unlink/BackgroundTask cleanup found
3 FileResponse uses BackgroundTask for cleanup FAIL No BackgroundTask attached

Summary: PASSED=0 FAILED=3

Fix

  • Adding from starlette.background import BackgroundTask import
  • Attaching background=BackgroundTask(os.unlink, tmp_path) to the FileResponse

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions