diff --git a/src/invidious/playlists.cr b/src/invidious/playlists.cr
index 1279486e..6015e5ec 100644
--- a/src/invidious/playlists.cr
+++ b/src/invidious/playlists.cr
@@ -190,23 +190,27 @@ def fetch_playlist(plid, locale)
   description_html ||= document.xpath_node(%q(//span[@class="pl-header-description-text"]))
   description_html, description = html_to_content(description_html)
 
-  anchor = document.xpath_node(%q(//ul[@class="pl-header-details"])).not_nil!
-  author = anchor.xpath_node(%q(.//li[1]/a)).not_nil!.content
+  # YouTube allows anonymous playlists, so most of this can be empty or optional
+  anchor = document.xpath_node(%q(//ul[@class="pl-header-details"]))
+  author = anchor.try &.xpath_node(%q(.//li[1]/a)).try &.content
+  author ||= ""
   author_thumbnail = document.xpath_node(%q(//img[@class="channel-header-profile-image"])).try &.["src"]
   author_thumbnail ||= ""
-  ucid = anchor.xpath_node(%q(.//li[1]/a)).not_nil!["href"].split("/")[-1]
+  ucid = anchor.try &.xpath_node(%q(.//li[1]/a)).try &.["href"].split("/")[-1]
+  ucid ||= ""
 
-  video_count = anchor.xpath_node(%q(.//li[2])).not_nil!.content.gsub(/\D/, "").to_i
-  views = anchor.xpath_node(%q(.//li[3])).not_nil!.content.delete("No views, ")
-  if views.empty?
-    views = 0_i64
+  video_count = anchor.try &.xpath_node(%q(.//li[2])).try &.content.gsub(/\D/, "").to_i?
+  video_count ||= 0
+  views = anchor.try &.xpath_node(%q(.//li[3])).try &.content.delete("No views, ").to_i64?
+  views ||= 0_i64
+
+  updated = anchor.try &.xpath_node(%q(.//li[4])).try &.content.lchop("Last updated on ").lchop("Updated ")
+  if updated
+    updated = decode_date(updated)
   else
-    views = views.to_i64
+    updated = Time.now
   end
 
-  updated = anchor.xpath_node(%q(.//li[4])).not_nil!.content.lchop("Last updated on ").lchop("Updated ")
-  updated = decode_date(updated)
-
   playlist = Playlist.new(
     title: title,
     id: plid,