Nobody has started this yet — be first.
Business impact
Every account on this service has a configured hard cap on upload size (IMAGESERVICE_MAX_UPLOAD_SIZE_BYTES, 10 MiB by default). A user whose file happens to be exactly at that limit -- not over it, exactly at it -- gets a 413 FILE_TOO_LARGE error today. From the user's point of view this looks like the file-size checker in the app is simply wrong: their file "is" the limit, and it's being told it exceeds the limit. This shows up as confusing support tickets ("your site says 10MB but rejects my 10MB file") that are hard for support staff to explain, because the stated limit and the actual enforced limit are silently off by one byte.
Problem
POST /images streams the upload to disk in chunks and enforces max_upload_size_bytes while streaming. A file whose total size is exactly equal to the configured cap is currently rejected with 413, identically to a file that is over the cap.
Current behavior
A file whose size is exactly IMAGESERVICE_MAX_UPLOAD_SIZE_BYTES gets rejected with 413 FILE_TOO_LARGE, indistinguishable from a file that is genuinely over the limit.
Expected behavior
A file whose total size is less than or equal to max_upload_size_bytes must be accepted (201). Only a file whose size is strictly greater than the cap should be rejected with 413 FILE_TOO_LARGE.
Steps to reproduce
cd fastapi/image_service && source .venv/bin/activate # or your own venv python3 - <<'PY' from fastapi.testclient import TestClient from app.dependencies import Settings from app.main import create_app import io from PIL import Image
img = Image.new("RGB", (40, 40), (10, 20, 30)) buf = io.BytesIO(); img.save(buf, format="PNG") data = buf.getvalue()
settings = Settings(upload_dir="/tmp/t01/u", database_url="sqlite:////tmp/t01/db.sqlite3", max_upload_size_bytes=len(data)) client = TestClient(create_app(settings)) resp = client.post("/images", files={"file": ("boundary.png", data, "image/png")}) print(resp.status_code, resp.json()) PY
Why this matters
Settings.max_upload_size_bytes (app/dependencies.py) is documented as a hard cap "enforced while streaming an upload to disk" -- a cap, not an exclusive upper bound. StorageBackend.save_upload's abstract docstring (app/storage.py) is explicit about the intended semantics: raise FileTooLargeError "as soon as max_bytes is exceeded" -- i.e. strictly greater than, not greater-than-or-equal-to. The implementation currently disagrees with its own contract.
Suggested approach
Look at the running-total comparison inside the while True: chunk-reading loop in LocalDiskStorage.save_upload (app/storage.py). Compare it carefully against the docstring's own wording ("exceeded") and against what Settings.max_upload_size_bytes's doc comment promises callers. Fix the one comparison operator so a total exactly equal to the cap is accepted.
Acceptance criteria
Verification
pytest practicetickets/test_ticket01_upload_size_boundary.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b fix/upload-size-boundaryFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/upload-size-boundarySubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign indocker-compose.yml./app./testsuvicorn --reload/app/dataDockerfile.proddocker compose -f docker-compose.prod.yml up --build -dWithout Docker:
cd fastapi/image_service
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env # optional, defaults work out of the box
uvicorn app.main:app --reload # -> http://127.0.0.1:8000/docs
Work the tickets in practicetickets/ (TICKET_01 through TICKET_07); each names one dedicated pytest file:
pytest practicetickets/test_ticket01_upload_size_boundary.py -v # a single ticket
./practicetickets/run_tickets.sh # all 7, clean pass/fail summary
Run run_tickets.sh from anywhere -- it cds to the project root itself before running, since app/main.py resolves its SQLite file and upload directory relative to the working directory at import time. pytest -q tests/ (the project's own pre-existing suite, separate from practicetickets/) is also worth running before and after each fix -- three of the four bugs (tickets 02, 04, 06) also break specific pre-existing tests there, and fixing the ticket correctly should make those green again too, with zero changes needed inside tests/ itself.
Level 1
Fix a bug
Read existing behaviour, correct it.