diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@
     invalidating cache properly) also for prompt (will not invalidate
     part1-only caches if there is no session key)
 *   **0.2.6.1 Bugfix**: Fix bug in prompt cache invalidation
+*   **0.2.6.2 Bugfix**: HTML parser for articles (for prompt API calls) now
+    more robust, adjusting for more malformed HTML from site.
 
 Version 0.2.5.0
 ---------------
diff --git a/advent-of-code-api.cabal b/advent-of-code-api.cabal
--- a/advent-of-code-api.cabal
+++ b/advent-of-code-api.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 7c78803c08846a438e524f068966ad9074ebfd7ce45ca61c3e10b611c820e23a
+-- hash: 29142f7dd051872d37ae0e834ec0dd60a68dc650d60dfa4d913add426cceb3c3
 
 name:           advent-of-code-api
-version:        0.2.6.1
+version:        0.2.6.2
 synopsis:       Advent of Code REST API bindings and servant API
 description:    Haskell bindings for Advent of Code REST API and a servant API.  Please use
                 responsibly! See README.md or "Advent" module for an introduction and
diff --git a/src/Advent/API.hs b/src/Advent/API.hs
--- a/src/Advent/API.hs
+++ b/src/Advent/API.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE CPP                   #-}
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE LambdaCase            #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings     #-}
 {-# LANGUAGE RecordWildCards       #-}
@@ -53,27 +54,27 @@
 import           Data.Char
 import           Data.Finite
 import           Data.Foldable
-import           Data.List.NonEmpty     (NonEmpty(..))
-import           Data.Map               (Map)
+import           Data.List.NonEmpty        (NonEmpty(..))
+import           Data.Map                  (Map)
 import           Data.Maybe
 import           Data.Ord
 import           Data.Proxy
-import           Data.Text              (Text)
+import           Data.Text                 (Text)
 import           Data.Time.Format
 import           Data.Time.LocalTime
 import           GHC.TypeLits
 import           Servant.API
 import           Servant.Client
-import           Text.HTML.TagSoup.Tree (TagTree(..))
-import           Text.Read              (readMaybe)
-import qualified Data.ByteString.Lazy   as BSL
-import qualified Data.List.NonEmpty     as NE
-import qualified Data.Map               as M
-import qualified Data.Text              as T
-import qualified Data.Text.Encoding     as T
-import qualified Network.HTTP.Media     as M
-import qualified Text.HTML.TagSoup      as H
-import qualified Text.HTML.TagSoup.Tree as H
+import           Text.HTML.TagSoup.Tree    (TagTree(..))
+import           Text.Read                 (readMaybe)
+import qualified Data.ByteString.Lazy      as BSL
+import qualified Data.List.NonEmpty        as NE
+import qualified Data.Map                  as M
+import qualified Data.Text                 as T
+import qualified Data.Text.Encoding        as T
+import qualified Network.HTTP.Media        as M
+import qualified Text.HTML.TagSoup         as H
+import qualified Text.HTML.TagSoup.Tree    as H
 
 #if !MIN_VERSION_base(4,11,0)
 import           Data.Semigroup ((<>))
@@ -276,17 +277,32 @@
     -> [Text]
 processHTML tag = mapMaybe getTag
                . H.universeTree
-               . H.parseTree
-               . cleanDoubleTitle
+               . H.tagTree
+               . cleanTags
+               . H.parseTags
   where
     getTag :: TagTree Text -> Maybe Text
     getTag (TagBranch n _ ts) = H.renderTree ts <$ guard (n == T.pack tag)
     getTag _                  = Nothing
-    -- 2016 Day 2 Part 2 has a malformed `<span>...</title>` tag that
-    -- causes tagsoup to choke.  this converts all </title> except for the
-    -- first one to be <span>.
-    cleanDoubleTitle :: Text -> Text
-    cleanDoubleTitle t = case T.splitOn "</title>" t of
-        x:xs -> x <> "</title>" <> T.intercalate "</span>" xs
-        []   -> ""      -- this shouldn't ever happen because splitOn is always non-empty
 
+-- | Some days, including:
+--
+-- * 2015 Day 6 Part 1
+-- * 2016 Day 2 Part 2
+--
+-- Have malformed HTML tags; the first has @<p></code>@ and the second has
+-- @<span></title>@.  This function cleans up all tags so that any closing
+-- tags ignore their actual tag type and instead close the last opened tag
+-- (if there is any).  If no tag is currently open then it just leaves it
+-- unchanged.
+cleanTags
+    :: [H.Tag str]
+    -> [H.Tag str]
+cleanTags = flip evalState [] . mapM go
+  where
+    go t = case t of
+      H.TagOpen n _ -> t <$ modify (n:)
+      H.TagClose _  -> get >>= \case
+        []   -> pure t
+        m:ms -> H.TagClose m <$ put ms
+      _             -> pure t
