diff --git a/rattletrap.cabal b/rattletrap.cabal
--- a/rattletrap.cabal
+++ b/rattletrap.cabal
@@ -1,7 +1,7 @@
 cabal-version: >= 1.10
 
 name: rattletrap
-version: 9.3.2
+version: 9.3.3
 synopsis: Parse and generate Rocket League replays.
 description: Rattletrap parses and generates Rocket League replays.
 
@@ -23,26 +23,27 @@
 library
   autogen-modules: Paths_rattletrap
   build-depends:
-    base >= 4.12.0 && < 4.15
-    , aeson >= 1.3.1 && < 1.6
-    , aeson-pretty >= 0.8.7 && < 0.9
-    , binary >= 0.8.5 && < 0.9
-    , bytestring >= 0.10.8 && < 0.11
+    base >= 4.14.1 && < 4.15
+    , aeson >= 1.5.5 && < 1.6
+    , aeson-pretty >= 0.8.8 && < 0.9
+    , binary >= 0.8.8 && < 0.9
+    , bytestring >= 0.10.12 && < 0.11
     , caerbannog >= 0.6.0 && < 0.7
-    , containers >= 0.5.11&& < 0.7
+    , containers >= 0.6.2 && < 0.7
     , filepath >= 1.4.2 && < 1.5
-    , http-client >= 0.5.13 && < 0.8
+    , http-client >= 0.6.4 && < 0.8
     , http-client-tls >= 0.3.5 && < 0.4
     , scientific >= 0.3.6 && < 0.4
-    , template-haskell >= 2.13.0 && < 2.17
-    , text >= 1.2.3 && < 1.3
-    , transformers >= 0.5.5 && < 0.6
+    , template-haskell >= 2.16.0 && < 2.17
+    , text >= 1.2.4 && < 1.3
+    , transformers >= 0.5.6 && < 0.6
   default-language: Haskell2010
   exposed-modules: Rattletrap
   ghc-options:
     -Weverything
     -Wno-all-missed-specialisations
     -Wno-implicit-prelude
+    -Wno-missing-deriving-strategies
     -Wno-missing-exported-signatures
     -Wno-missing-import-lists
     -Wno-safe
@@ -288,10 +289,6 @@
     Rattletrap.Utility.Bytes
     Rattletrap.Utility.Crc
     Rattletrap.Utility.Helper
-
-  if impl(ghc >= 8.8)
-    ghc-options:
-      -Wno-missing-deriving-strategies
 
   if impl(ghc >= 8.10)
     ghc-options:
diff --git a/src/lib/Rattletrap/Encode/Content.hs b/src/lib/Rattletrap/Encode/Content.hs
--- a/src/lib/Rattletrap/Encode/Content.hs
+++ b/src/lib/Rattletrap/Encode/Content.hs
@@ -19,18 +19,33 @@
 import qualified Data.Binary as Binary
 import qualified Data.Binary.Bits.Put as BinaryBits
 import qualified Data.Binary.Put as Binary
+import qualified Data.ByteString as Bytes
 import qualified Data.ByteString.Lazy as LazyBytes
 
 putContent :: Content -> Binary.Put
 putContent content = do
   putList putText (contentLevels content)
   putList putKeyFrame (contentKeyFrames content)
-  let streamSize = contentStreamSize content
-  putWord32 streamSize
   let
     stream = LazyBytes.toStrict
       (Binary.runPut (BinaryBits.runBitPut (putFrames (contentFrames content)))
       )
+    -- This is a little strange. When parsing a binary replay, the stream size
+    -- is given before the stream itself. When generating the JSON, the stream
+    -- size is included. That allows a bit-for-bit identical binary replay to
+    -- be generated from the JSON. However if you modify the JSON before
+    -- converting it back into binary, the stream size might be different.
+    --
+    -- If it was possible to know how much padding the stream required without
+    -- carrying it along as extra data on the side, this logic could go away.
+    -- Unforunately that isn't currently known. See this issue for details:
+    -- <https://github.com/tfausak/rattletrap/issues/171>.
+    expectedStreamSize = contentStreamSize content
+    actualStreamSize = Word32le . fromIntegral $ Bytes.length stream
+    streamSize = Word32le $ max
+      (word32leValue expectedStreamSize)
+      (word32leValue actualStreamSize)
+  putWord32 streamSize
   Binary.putByteString
     (reverseBytes (padBytes (word32leValue streamSize) stream))
   putList putMessage (contentMessages content)
