diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
 Changelog
 =========
 
+Version 0.2.2.1
+---------------
+
+*November 19, 2019*
+
+<https://github.com/mstksg/advent-of-code-api/releases/tag/v0.2.2.1>
+
+*   Fixed prompt parser that would fail on 2016 Day 2 Part 2 because of a
+    malformed `<span>...</title>` tag pair in the prompt HTML
+
 Version 0.2.2.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: 7b89429fd3572b2daa643989846c5ea1edc56e85c3dcb14faae56bc4b97d3b42
+-- hash: bc9fa15aa4120fc135cccf4203d4b3e26d035a13f3ade163809aee30e14ebc42
 
 name:           advent-of-code-api
-version:        0.2.2.0
+version:        0.2.2.1
 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.hs b/src/Advent.hs
--- a/src/Advent.hs
+++ b/src/Advent.hs
@@ -337,7 +337,7 @@
 saverLoader = \case
     AoCPrompt d -> SL { _slSave = either (const Nothing) (Just . encodeMap)
                       , _slLoad = \str ->
-                          let mp = decodeMap str
+                          let mp     = decodeMap str
                               hasAll = S.null (expectedParts d `S.difference` M.keysSet mp)
                           in  Right mp <$ guard hasAll
                       }
diff --git a/src/Advent/API.hs b/src/Advent/API.hs
--- a/src/Advent/API.hs
+++ b/src/Advent/API.hs
@@ -320,10 +320,18 @@
             . mapMaybe isArticle
             . H.universeTree
             . H.parseTree
+            . cleanDoubleTitle
   where
     isArticle :: TagTree Text -> Maybe [TagTree Text]
     isArticle (TagBranch n _ ts) = ts <$ guard (n == "article")
     isArticle _                  = 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
 
 -- | Parse 'Text' into a 'SubmitRes'.
 parseSubmitRes :: Text -> SubmitRes
