Bug: Missing player photo returns 404 — uploads/photos storage gap #374

Closed
opened 2026-04-07 17:04:29 +00:00 by forgejo_admin · 3 comments

Type

Bug

Lineage

Standalone — discovered during CRM monitoring session 2026-04-07. Observed in access logs from Marcus's iPhone browsing.

Repo

forgejo_admin/basketball-api (API-only fix — no cross-repo changes needed)

What Broke

GET /uploads/photos/4685d046593942f48013a484bb3739e9.jpg returns 404 Not Found. At least one player has a photo reference in the database that doesn't exist on disk. Another photo (d8de178c3ca94c738d561ec998196006.jpg) returned 304 (cached, exists).

The storage infrastructure is sound — PVC is correctly wired at /data/uploads/photos with RWO access mode and Recreate deployment strategy. The issue is either an orphaned DB reference or a file that was lost.

ImageStaticFiles in src/basketball_api/static.py serves raw 404s for missing files with no fallback. The tryouts route (src/basketball_api/routes/tryouts.py:195-201) already has a silhouette SVG placeholder pattern, but player profile and admin views do not.

7+ consumers of photo_url across basketball-api and westside-app.

Repro Steps

  1. Browse admin teams or players list on the westside CRM
  2. Observe: one or more player avatar images show broken/missing
  3. API log: GET /uploads/photos/4685d046593942f48013a484bb3739e9.jpg 404

Expected Behavior

All photos referenced in the database exist in the uploads directory. When a photo file is missing, the API returns a default placeholder image instead of 404.

Environment

  • Cluster/namespace: prod / basketball-api
  • Service version/commit: basketball-api-6d89b9fc4f-ldjvc
  • Related alerts: none

Acceptance Criteria

  • Identify which player has the orphaned photo reference (4685d046593942f48013a484bb3739e9.jpg)
  • Either restore the photo or clear the stale DB reference
  • ImageStaticFiles returns a default silhouette placeholder for missing photos instead of 404
  • Adopt the existing placeholder pattern from tryouts.py:195-201
  • No regressions in existing photo upload/serve flow

File Targets

  • src/basketball_api/static.py — add fallback placeholder for missing photos
  • src/basketball_api/routes/tryouts.py:195-201 — reference implementation for silhouette pattern
  • DB query to find orphaned photo reference

Discovered Scope (separate ticket)

  • src/basketball_api/routes/register.py:1069 duplicates upload.py upload logic — DRY violation, track separately
  • westside-basketball — project this affects
  • story:WS-S24 — "As a player, I want to view my profile with photo and team info"
### Type Bug ### Lineage Standalone — discovered during CRM monitoring session 2026-04-07. Observed in access logs from Marcus's iPhone browsing. ### Repo `forgejo_admin/basketball-api` (API-only fix — no cross-repo changes needed) ### What Broke `GET /uploads/photos/4685d046593942f48013a484bb3739e9.jpg` returns **404 Not Found**. At least one player has a photo reference in the database that doesn't exist on disk. Another photo (`d8de178c3ca94c738d561ec998196006.jpg`) returned 304 (cached, exists). The storage infrastructure is sound — PVC is correctly wired at `/data/uploads/photos` with RWO access mode and Recreate deployment strategy. The issue is either an orphaned DB reference or a file that was lost. `ImageStaticFiles` in `src/basketball_api/static.py` serves raw 404s for missing files with no fallback. The tryouts route (`src/basketball_api/routes/tryouts.py:195-201`) already has a silhouette SVG placeholder pattern, but player profile and admin views do not. 7+ consumers of `photo_url` across basketball-api and westside-app. ### Repro Steps 1. Browse admin teams or players list on the westside CRM 2. Observe: one or more player avatar images show broken/missing 3. API log: `GET /uploads/photos/4685d046593942f48013a484bb3739e9.jpg 404` ### Expected Behavior All photos referenced in the database exist in the uploads directory. When a photo file is missing, the API returns a default placeholder image instead of 404. ### Environment - Cluster/namespace: prod / basketball-api - Service version/commit: `basketball-api-6d89b9fc4f-ldjvc` - Related alerts: none ### Acceptance Criteria - [ ] Identify which player has the orphaned photo reference (`4685d046593942f48013a484bb3739e9.jpg`) - [ ] Either restore the photo or clear the stale DB reference - [ ] `ImageStaticFiles` returns a default silhouette placeholder for missing photos instead of 404 - [ ] Adopt the existing placeholder pattern from `tryouts.py:195-201` - [ ] No regressions in existing photo upload/serve flow ### File Targets - `src/basketball_api/static.py` — add fallback placeholder for missing photos - `src/basketball_api/routes/tryouts.py:195-201` — reference implementation for silhouette pattern - DB query to find orphaned photo reference ### Discovered Scope (separate ticket) - `src/basketball_api/routes/register.py:1069` duplicates `upload.py` upload logic — DRY violation, track separately ### Related - `westside-basketball` — project this affects - `story:WS-S24` — "As a player, I want to view my profile with photo and team info"
Author
Owner

Scope Review: NEEDS_REFINEMENT

Review note: review-879-2026-04-07

Traceability is mostly solid (story:WS-S24 verified, Forgejo issue open), but arch note is missing and issue body template completeness could not be verified due to tooling gap.

Issues found:

  • [SCOPE] Architecture note arch-basketball-api does not exist in pal-e-docs -- needs to be created
  • [BODY] Verify all template-issue-bug sections are present (Type, Lineage, Repo, What Broke, Repro Steps, Expected Behavior, Environment, AC, Related) -- reviewer could not read issue body
  • [BODY] AC should explicitly state whether fix is API-only (ImageStaticFiles 404 override) or requires frontend changes in westside-app
  • [BODY] File targets should list: static.py, test file for 404 fallback, possibly upload.py
  • [BODY] DRY concern: register.py:1069 duplicates upload.py logic -- track as discovered scope, not in this bug's scope

Codebase findings:

  • PVC storage is properly wired (1Gi RWO, mounted at /data/uploads/photos)
  • ImageStaticFiles in static.py handles MIME types but has no 404 fallback
  • Tryouts roster (tryouts.py:195) already has silhouette SVG fallback -- player profile and admin views do not
  • 7+ consumers of photo_url across basketball-api and westside-app
## Scope Review: NEEDS_REFINEMENT Review note: `review-879-2026-04-07` Traceability is mostly solid (story:WS-S24 verified, Forgejo issue open), but arch note is missing and issue body template completeness could not be verified due to tooling gap. **Issues found:** - `[SCOPE]` Architecture note `arch-basketball-api` does not exist in pal-e-docs -- needs to be created - `[BODY]` Verify all template-issue-bug sections are present (Type, Lineage, Repo, What Broke, Repro Steps, Expected Behavior, Environment, AC, Related) -- reviewer could not read issue body - `[BODY]` AC should explicitly state whether fix is API-only (ImageStaticFiles 404 override) or requires frontend changes in westside-app - `[BODY]` File targets should list: `static.py`, test file for 404 fallback, possibly `upload.py` - `[BODY]` DRY concern: `register.py:1069` duplicates `upload.py` logic -- track as discovered scope, not in this bug's scope **Codebase findings:** - PVC storage is properly wired (1Gi RWO, mounted at /data/uploads/photos) - `ImageStaticFiles` in `static.py` handles MIME types but has no 404 fallback - Tryouts roster (`tryouts.py:195`) already has silhouette SVG fallback -- player profile and admin views do not - 7+ consumers of photo_url across basketball-api and westside-app
Author
Owner

Scope refinement (post review-879-2026-04-07):

  • Clarified API-only fix (no cross-repo changes)
  • Added file targets: static.py, reference pattern in tryouts.py:195-201
  • Noted 7+ consumers of photo_url
  • Tracked discovered DRY violation (register.py:1069) as separate scope
**Scope refinement (post review-879-2026-04-07):** - Clarified API-only fix (no cross-repo changes) - Added file targets: `static.py`, reference pattern in `tryouts.py:195-201` - Noted 7+ consumers of `photo_url` - Tracked discovered DRY violation (`register.py:1069`) as separate scope
Author
Owner

Scope Review: READY

Review note: review-879-2026-04-07 (updated from NEEDS_REFINEMENT to READY)

Re-review after refinement. All 4 [BODY] recommendations from v1 addressed. File targets verified against codebase -- static.py is the fix target (override 404 to serve placeholder), tryouts.py silhouette pattern is the reference implementation.

Remaining [SCOPE]: Create architecture note arch-basketball-api (organizational gap, not a ticket blocker -- carried forward).

Ticket is ready for dispatch. Estimated agent time: under 5 minutes. No decomposition needed.

## Scope Review: READY Review note: `review-879-2026-04-07` (updated from NEEDS_REFINEMENT to READY) Re-review after refinement. All 4 [BODY] recommendations from v1 addressed. File targets verified against codebase -- static.py is the fix target (override 404 to serve placeholder), tryouts.py silhouette pattern is the reference implementation. **Remaining [SCOPE]:** Create architecture note `arch-basketball-api` (organizational gap, not a ticket blocker -- carried forward). Ticket is ready for dispatch. Estimated agent time: under 5 minutes. No decomposition needed.
forgejo_admin 2026-04-07 17:45:59 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/basketball-api#374
No description provided.