From 9c7beec44311ef22cb7118b03a89fdda33893ad7 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 19 Jun 2026 04:16:23 +0200 Subject: [PATCH 1/2] fix(channels): reconcile an already-followed channel instead of erroring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subscribing to a channel the user already follows on YouTube (a local/YouTube desync — e.g. a stale import dropped the local row, so it resurfaced in the discovery list) made YouTube return 400 "subscription already exists", which the endpoint reported as a 502 — and the client shows a 502 as a transient "connection lost" blip, not the real reason. Treat the duplicate as success: record the subscription locally (the desired end state already holds; the next resync fills in the resource id). Surface any other YouTube error as 422 with a clear message, on both subscribe and unsubscribe, so it isn't mistaken for a connection drop. --- backend/app/routes/channels.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/app/routes/channels.py b/backend/app/routes/channels.py index f21ed47..d996047 100644 --- a/backend/app/routes/channels.py +++ b/backend/app/routes/channels.py @@ -313,7 +313,17 @@ def subscribe( if channel.details_synced_at is None: apply_channel_details(db, yt.get_channels([channel_id])) except YouTubeError as exc: - raise HTTPException(status_code=502, detail=f"YouTube subscribe failed: {exc}") + # Already following on YouTube but not recorded here (a desync — e.g. a stale import + # dropped the local row, so it resurfaced in discovery). That's not a failure: the end + # state the user wanted already holds, so record it locally and move on. The next + # subscription resync fills in the resource id. Any other YouTube error is a real + # problem — surface it with a clear message (422, not a 502 that the client would + # mistake for a transient "connection lost"). + msg = str(exc).lower() + if "already exists" in msg or "duplicate" in msg: + yt_sub_id = "" + else: + raise HTTPException(status_code=422, detail=f"YouTube couldn't subscribe you: {exc}") db.add( Subscription( user_id=user.id, @@ -349,7 +359,9 @@ def unsubscribe( with quota.attribute(user.id, "unsubscribe"), YouTubeClient(db, user) as yt: yt.delete_subscription(sub.yt_subscription_id) except YouTubeError as exc: - raise HTTPException(status_code=502, detail=f"YouTube unsubscribe failed: {exc}") + # 422 (a real, explainable error → speaking modal), not 502 which the client treats as + # a transient "connection lost" gateway blip. + raise HTTPException(status_code=422, detail=f"YouTube couldn't unsubscribe you: {exc}") db.delete(sub) db.commit() return {"unsubscribed": channel_id} From 403a5608dfb76dd85a776d2810bd4fbcb37fa870 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 19 Jun 2026 04:16:23 +0200 Subject: [PATCH 2/2] release: 0.11.1 --- VERSION | 2 +- frontend/src/lib/releaseNotes.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d9df1bb..af88ba8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.11.0 +0.11.1 diff --git a/frontend/src/lib/releaseNotes.ts b/frontend/src/lib/releaseNotes.ts index 6ed23ae..f67de59 100644 --- a/frontend/src/lib/releaseNotes.ts +++ b/frontend/src/lib/releaseNotes.ts @@ -14,6 +14,17 @@ export interface ReleaseEntry { } export const RELEASE_NOTES: ReleaseEntry[] = [ + { + version: "0.11.1", + date: "2026-06-19", + summary: "A fix for subscribing from the discovery list.", + fixes: [ + "Subscribing to a channel you already follow on YouTube now simply records it here instead of showing a misleading “connection lost” error — which also clears the case where such a channel kept reappearing under “Discover from playlists”.", + ], + chores: [ + "YouTube-side action failures now surface as a clear message instead of being mistaken for a transient connection drop.", + ], + }, { version: "0.11.0", date: "2026-06-19",