Skip to content

Propagate backend errors as structured JSON instead of swallowing them - #2

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1787719678-error-handling
Open

Propagate backend errors as structured JSON instead of swallowing them#2
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1787719678-error-handling

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Summary

The backend swallowed most upstream failures: print() + return None/return False, a bare except:, and per-route except Exception as e: return jsonify({"error": str(e)}), 500. The visible symptoms were an expired Spotify token surfacing as 404 "No songs found on Spotify matching the criteria", Get_Playlists returning Spotify's error body with HTTP 200, a login that "succeeded" without a spotify_user_id, and a silently failed cover upload reported as full success.

This adds a small error taxonomy (backend/errors.py) and lets those errors propagate to app-wide JSON handlers, so status codes are meaningful (401 re-auth, 429 rate limited, 502 upstream, 400 bad input) and unexpected exceptions log a traceback while returning a generic 500 instead of a Flask HTML page.

# backend/errors.py
AppError(message, status_code, details)  # -> {"error": ..., "details": ...}
ConfigError(500) SpotifyAuthError(401) SpotifyRateLimitError(429) SpotifyAPIError(502) AIServiceError(502)
register_error_handlers(app)  # AppError / HTTPException / Exception -> JSON, logged

All Spotify calls now go through one SpotifyService._request that applies a timeout, maps RequestException/401/429/4xx-5xx onto those errors, and extracts Spotify's error.message as details — replacing the mix of raise_for_status(), unchecked response.json(), and status checks that only existed in some methods.

Behavioral changes worth noting:

  • search_track still returns None for "no match", but auth/rate-limit/network failures now raise. Generate_Preview collects per-song failures, and when every search failed it re-raises the underlying cause (preferring auth, then rate limit) instead of reporting 404 "no songs found"; partial failures succeed with a warning field.
  • except: around int(playlistLength) silently defaulted to 20; it now returns 400 for non-numeric or out-of-range values (1..100).
  • /callback treats a failed profile fetch as fatal (it previously logged a warning and left the session without spotify_user_id), and rejects a token response with no access_token instead of storing None.
  • /auth/status reports why a refresh failed rather than a bare {"authenticated": false}.
  • Create_Playlist still doesn't fail when only the cover upload fails, but returns cover_uploaded: false plus a warning; add_tracks_to_playlist chunks at Spotify's 100-URI limit.
  • AI responses: JSON decode errors, blocked/empty candidates, and entries missing name/artist now raise AIServiceError (or are dropped with a warning) instead of being wrapped in a generic Exception.
  • Startup logs missing required env vars, and the session directory is created in create_app() so it also exists under a WSGI server (it was only created in run.py's __main__ block).

Frontend: SpotifyPlaylistGenerator showed nothing on failure — a failed save left the user on the preview screen with only a console log. Save/auth-check/logout failures now raise a destructive toast, and the no-op try/catch around handleGeneratePlaylist was replaced with a toast on invalid payloads.

Testing

Verified via a throwaway harness driving app.test_client() with requests.request and AIService mocked (not committed): expired token during search → 401, Spotify 500 → 502, connection error → 502, non-numeric playlistLength → 400, unexpected exception → JSON 500 with logged traceback, cover-upload 403 → 200 with cover_uploaded: false + warning, missing config → 500 Server misconfiguration.

Frontend changes are not type-checked: UI_Front/src/lib/api.ts is not in the repo — the root .gitignore's Python lib/ rule excludes it, so tsc/vite build cannot resolve @/lib/api in a fresh clone.

Link to Devin session: https://app.devin.ai/sessions/5b7fbfb5f4d14d05b2e0ea9fd64b9d14
Requested by: @praneels2005

@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant