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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions