Propagate backend errors as structured JSON instead of swallowing them - #2
Open
devin-ai-integration[bot] wants to merge 1 commit into
Open
Propagate backend errors as structured JSON instead of swallowing them#2devin-ai-integration[bot] wants to merge 1 commit into
devin-ai-integration[bot] wants to merge 1 commit into
Conversation
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The backend swallowed most upstream failures:
print()+return None/return False, a bareexcept:, and per-routeexcept Exception as e: return jsonify({"error": str(e)}), 500. The visible symptoms were an expired Spotify token surfacing as404 "No songs found on Spotify matching the criteria",Get_Playlistsreturning Spotify's error body with HTTP 200, a login that "succeeded" without aspotify_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.All Spotify calls now go through one
SpotifyService._requestthat applies a timeout, mapsRequestException/401/429/4xx-5xx onto those errors, and extracts Spotify'serror.messageasdetails— replacing the mix ofraise_for_status(), uncheckedresponse.json(), and status checks that only existed in some methods.Behavioral changes worth noting:
search_trackstill returnsNonefor "no match", but auth/rate-limit/network failures now raise.Generate_Previewcollects 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 awarningfield.except:aroundint(playlistLength)silently defaulted to 20; it now returns 400 for non-numeric or out-of-range values (1..100)./callbacktreats a failed profile fetch as fatal (it previously logged a warning and left the session withoutspotify_user_id), and rejects a token response with noaccess_tokeninstead of storingNone./auth/statusreports why a refresh failed rather than a bare{"authenticated": false}.Create_Playliststill doesn't fail when only the cover upload fails, but returnscover_uploaded: falseplus awarning;add_tracks_to_playlistchunks at Spotify's 100-URI limit.name/artistnow raiseAIServiceError(or are dropped with a warning) instead of being wrapped in a genericException.create_app()so it also exists under a WSGI server (it was only created inrun.py's__main__block).Frontend:
SpotifyPlaylistGeneratorshowed 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-optry/catcharoundhandleGeneratePlaylistwas replaced with a toast on invalid payloads.Testing
Verified via a throwaway harness driving
app.test_client()withrequests.requestandAIServicemocked (not committed): expired token during search → 401, Spotify 500 → 502, connection error → 502, non-numericplaylistLength→ 400, unexpected exception → JSON 500 with logged traceback, cover-upload 403 → 200 withcover_uploaded: false+ warning, missing config → 500Server misconfiguration.Frontend changes are not type-checked:
UI_Front/src/lib/api.tsis not in the repo — the root.gitignore's Pythonlib/rule excludes it, sotsc/vite buildcannot resolve@/lib/apiin a fresh clone.Link to Devin session: https://app.devin.ai/sessions/5b7fbfb5f4d14d05b2e0ea9fd64b9d14
Requested by: @praneels2005