{"record":{"id":"d3812b180308ece7","repo":"gitbutlerapp/gitbutler","slug":"failed-to-fetch-from-rss-feed","errorCode":null,"errorMessage":"Failed to fetch from RSS feed:","messagePattern":"Failed to fetch from RSS feed:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"apps/web/src/lib/youtube.ts","lineNumber":108,"sourceCode":"export async function fetchPlaylistVideos(playlistId: string): Promise<YouTubePlaylist> {\n\ttry {\n\t\tconst response = await fetch(`${env.PUBLIC_APP_HOST}api/youtube/playlist`, {\n\t\t\t// Add timeout to prevent hanging\n\t\t\tsignal: AbortSignal.timeout(10000),\n\t\t});\n\n\t\tif (response.ok) {\n\t\t\tconst data = (await response.json()) as { videos: APIYouTubeVideo[] };\n\t\t\tconst videos = data.videos.map(mapAPIToYouTubeVideo);\n\t\t\treturn {\n\t\t\t\tid: playlistId,\n\t\t\t\ttitle: \"GitButler Feature Updates\",\n\t\t\t\tdescription: \"Latest GitButler tutorials, feature demonstrations, and updates\",\n\t\t\t\tvideos,\n\t\t\t};\n\t\t}\n\t} catch (error) {\n\t\tconsole.warn(\"Failed to fetch from RSS feed:\", error);\n\t}\n\n\t// Fallback to hardcoded playlist data for the specific GitButler playlist\n\treturn getGitButlerPlaylistFallback(playlistId);\n}\n\n/**\n * Fallback data with actual GitButler video information\n * This can be updated manually when new videos are added to the playlist\n */\nfunction getGitButlerPlaylistFallback(playlistId: string): YouTubePlaylist {\n\tconst videos: YouTubeVideo[] = [\n\t\t{\n\t\t\tid: \"NOYK7LTFvZM\",\n\t\t\ttitle: \"Using Cursor Hooks for automatic version control\",\n\t\t\tdescription:\n\t\t\t\t\"Here we demonstrate how to use GitButler with the new Cursor Hooks functionality to automate creating branches for chat sessions and committing work as you go with smart commit messages. Never lose a step again.\",\n\t\t\tthumbnail: \"https://img.youtube.com/vi/NOYK7LTFvZM/maxresdefault.jpg\",","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/apps/web/src/lib/youtube.ts#L90-L126","documentation":"The web app fetches its YouTube playlist JSON from an API; on a non-ok response or a thrown fetch/JSON error it warns and falls back to getGitButlerPlaylistFallback(), a hardcoded curated list keyed by playlistId. The page always renders a playlist, at the cost of serving stale videos whenever the fetch path fails.","triggerScenarios":"fetch() rejects (offline, DNS failure, timeout), the API returns non-200 (4xx/5xx), or response.json() throws on a malformed body — each lands in the catch at apps/web/src/lib/youtube.ts:108 and activates the hardcoded fallback.","commonSituations":"Offline browsing or flaky mobile networks; the playlist API rate-limited or down; CDN or edge misconfiguration; an API response shape change breaking mapAPIToYouTubeVideo.","solutions":["Verify network and the API response status in devtools, then retry when back online","If videos look stale, update the hardcoded fallback list — it is curated manually by design","Fix or escalate the API if it consistently returns non-ok","Cache the last successful response client-side so the fallback stays fresher"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"async function reachable(url: string): Promise<boolean> {\n\ttry {\n\t\tconst res = await fetch(url, { method: \"HEAD\" });\n\t\treturn res.ok;\n\t} catch {\n\t\treturn false;\n\t}\n}","typeGuard":"function isNetworkFailure(e: unknown): boolean {\n\treturn e instanceof TypeError; // fetch rejects with TypeError on network-level failure\n}","tryCatchPattern":"try {\n\treturn await fetchPlaylist(playlistId);\n} catch (error) {\n\tconsole.warn(\"Failed to fetch from RSS feed:\", error);\n\treturn getGitButlerPlaylistFallback(playlistId); // degrade, never throw\n}","preventionTips":["Always pair remote playlist data with a curated fallback, as this code does","Use AbortSignal.timeout() to fail fast instead of hanging","Cache the last successful payload in localStorage"],"tags":["typescript","fetch","network","fallback","youtube"],"backgroundTag":"fetch-request-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}