{"record":{"id":"3082737b103cf029","repo":"gitbutlerapp/gitbutler","slug":"invalid-playlist-url","errorCode":null,"errorMessage":"Invalid playlist URL","messagePattern":"Invalid playlist URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/web/src/routes/(home)/sections/FeatureUpdates.svelte","lineNumber":147,"sourceCode":"\n\t\treturn () => {\n\t\t\tclearTimeout(timeoutId);\n\t\t\tcarousel?.removeEventListener(\"scroll\", updateScrollState);\n\t\t};\n\t});\n\n\t$effect(() => {\n\t\tif (playlist && carousel) {\n\t\t\tsetTimeout(updateScrollState, 100);\n\t\t}\n\t});\n\n\t// Data loading\n\tonMount(async () => {\n\t\ttry {\n\t\t\tconst playlistId = extractPlaylistId(PLAYLIST_URL);\n\t\t\tif (!playlistId) {\n\t\t\t\tthrow new Error(\"Invalid playlist URL\");\n\t\t\t}\n\n\t\t\tplaylist = await fetchPlaylistVideos(playlistId);\n\t\t} catch (err) {\n\t\t\terror = err instanceof Error ? err.message : \"Failed to load videos\";\n\t\t\tconsole.error(\"Error loading playlist:\", err);\n\t\t} finally {\n\t\t\tisLoading = false;\n\t\t}\n\t});\n</script>\n\n<section class=\"feature-updates\">\n\t<SectionHeader>\n\t\t<i>Feature</i> updates\n\n\t\t{#snippet buttons()}\n\t\t\t<div class=\"feature-updates__all-demos\">","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/apps/web/src/routes/(home)/sections/FeatureUpdates.svelte#L129-L165","documentation":"Thrown in onMount when extractPlaylistId(PLAYLIST_URL) returns falsy. PLAYLIST_URL is a hardcoded constant in the component, so this is a broken build-time constant or a parser/regex regression, not user input; the catch block stores the message and the section renders its error state.","triggerScenarios":"The PLAYLIST_URL constant was edited and lost its valid list= query parameter, or extractPlaylistId's parsing (in $lib/youtube) changed and no longer matches the URL shape (extra si= param, different host form like youtube.com vs www.youtube.com).","commonSituations":"Someone swapped the featured-release playlist URL and pasted a watch?v= or channel URL by mistake; youtube helper refactor broke the list-param regex; the marketing playlist moved and the constant was updated with a malformed URL.","solutions":["Restore a URL of the form https://youtube.com/playlist?list=<ID> in the PLAYLIST_URL constant.","Add a unit test pinning extractPlaylistId against the exact constant value so edits fail in CI instead of at runtime.","Optionally hide the section when extraction fails rather than showing an error block."],"exampleFix":"// before\nconst PLAYLIST_URL =\n  \"https://youtube.com/playlist?list=PLNXkW_le40U7IH8qA5VPN6f01oC25LOj4&si=IRZbd5aBoNLWDH5g\";\n\n// after — fail loudly at module load if the constant stops parsing\nconst PLAYLIST_URL =\n  \"https://youtube.com/playlist?list=PLNXkW_le40U7IH8qA5VPN6f01oC25LOj4&si=IRZbd5aBoNLWDH5g\";\nif (!extractPlaylistId(PLAYLIST_URL)) {\n  throw new Error(\"PLAYLIST_URL no longer contains a playlist id — fix the constant\");\n}","handlingStrategy":"validation","validationCode":"const playlistId = extractPlaylistId(PLAYLIST_URL);\nif (!playlistId) {\n  error = \"\"; // hide the section instead of showing an error for a constant\n  console.error(\"PLAYLIST_URL is not a valid playlist URL:\", PLAYLIST_URL);\n  return;\n}","typeGuard":"function isValidPlaylistUrl(url: string): boolean {\n  return /[?&]list=[A-Za-z0-9_-]+/.test(url);\n}","tryCatchPattern":"try {\n  const playlistId = extractPlaylistId(PLAYLIST_URL);\n  if (!playlistId) throw new Error(\"Invalid playlist URL\");\n  playlist = await fetchPlaylistVideos(playlistId);\n} catch (err) {\n  error = err instanceof Error ? err.message : \"Failed to load videos\";\n}","preventionTips":["Pin extractPlaylistId with a unit test against the exact PLAYLIST_URL constant.","Treat a bad constant as a hide-the-section condition, not a user-facing error.","Validate playlist URLs in CI via a small config test."],"tags":["svelte","web","youtube","url-parsing","config-constant"],"backgroundTag":"missing-url-parameter","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}