diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,18 @@
 # ChangeLog for hpke
 
+## 0.2.0
+
+* Breaking change: `exportS` and `exportR` return `Either HPKEError Key`
+  rather than `Key`.  RFC 9180 section 5.3 allows an export of at most
+  `255 * Nh` octets and the length is the caller's to choose, so the refusal
+  now has somewhere to go.  Nothing was total here before -- a length beyond
+  65535 did not fit the two octets `LabeledExpand` writes it into, and
+  crypton raised -- and from crypton 2.0.0 the shorter limit raises as well.
+* Breaking change: `HPKEError` gains `ExportError`, which is what the above
+  reports.  It is appended, so the existing constructors are where they
+  were, but an exhaustive `case` without a wildcard will warn.
+* Supporting crypton 2.0.
+
 ## 0.1.0
 
 * Using "ram" instead of "memory".
diff --git a/Crypto/HPKE/Context.hs b/Crypto/HPKE/Context.hs
--- a/Crypto/HPKE/Context.hs
+++ b/Crypto/HPKE/Context.hs
@@ -28,7 +28,7 @@
     { seqRefS :: IORef Integer
     , sealS :: Seal
     , nonceBaseS :: Nonce
-    , expandS :: Info -> Int -> Key
+    , expandS :: Info -> Int -> Either HPKEError Key
     }
 
 -- | Context for receivers.
@@ -36,7 +36,7 @@
     { seqRefR :: IORef Integer
     , openR :: Open
     , nonceBaseR :: Nonce
-    , expandR :: Info -> Int -> Key
+    , expandR :: Info -> Int -> Either HPKEError Key
     }
 
 ----------------------------------------------------------------
@@ -76,12 +76,18 @@
 ----------------------------------------------------------------
 
 -- | Exporting secret.
-exportS :: ContextS -> Info -> Int -> Key
+--
+-- RFC 9180 section 5.3 allows a length of at most @255 * Nh@, where @Nh@ is
+-- the output of the KDF's hash; a longer one is 'Left' 'ExportError'.
+exportS :: ContextS -> Info -> Int -> Either HPKEError Key
 exportS ContextS{..} exporter_context len =
     expandS exporter_context len
 
 -- | Exporting secret.
-exportR :: ContextR -> Info -> Int -> Key
+--
+-- RFC 9180 section 5.3 allows a length of at most @255 * Nh@, where @Nh@ is
+-- the output of the KDF's hash; a longer one is 'Left' 'ExportError'.
+exportR :: ContextR -> Info -> Int -> Either HPKEError Key
 exportR ContextR{..} exporter_context len =
     expandR exporter_context len
 
@@ -91,7 +97,7 @@
     :: Key
     -> Nonce
     -> (Key -> Seal)
-    -> (Info -> Int -> Key)
+    -> (Info -> Int -> Either HPKEError Key)
     -> IO ContextS
 newContextS key nonce_base seal' expand = do
     seqref <- newIORef 0
@@ -109,7 +115,7 @@
     :: Key
     -> Nonce
     -> (Key -> Open)
-    -> (Info -> Int -> Key)
+    -> (Info -> Int -> Either HPKEError Key)
     -> IO ContextR
 newContextR key nonce_base open' expand = do
     seqref <- newIORef 0
diff --git a/Crypto/HPKE/KDF.hs b/Crypto/HPKE/KDF.hs
--- a/Crypto/HPKE/KDF.hs
+++ b/Crypto/HPKE/KDF.hs
@@ -12,6 +12,7 @@
 )
 where
 
+import Crypto.Hash.IO (hashDigestSize)
 import Crypto.Hash.Algorithms (
     HashAlgorithm,
     SHA256 (..),
@@ -27,8 +28,14 @@
 
 class KDF h where
     labeledExtract :: Suite -> Salt -> Label -> IKM -> PRK h
-    labeledExpand :: Suite -> PRK h -> Label -> Info -> Int -> Key
 
+    -- | RFC 9180 section 5.3 allows an output of at most @255 * Nh@ octets,
+    -- which is also what HKDF's counter can reach.  A longer one is refused
+    -- here rather than left to the HKDF underneath, whose way of saying so
+    -- is an exception.
+    labeledExpand
+        :: Suite -> PRK h -> Label -> Info -> Int -> Either HPKEError Key
+
 instance KDF SHA256 where
     labeledExtract = labeledExtract_
     labeledExpand = labeledExpand_
@@ -50,9 +57,20 @@
     labeled_ikm = "HPKE-v1" <> suite <> label <> ikm
 
 labeledExpand_
-    :: HashAlgorithm a => Suite -> PRK a -> Label -> Info -> Int -> Key
-labeledExpand_ suite prk label info len = HKDF.expand prk labeled_info len
+    :: forall a
+     . HashAlgorithm a
+    => Suite -> PRK a -> Label -> Info -> Int -> Either HPKEError Key
+labeledExpand_ suite prk label info len
+    | len < 0 || len > maxLen =
+        Left $
+            ExportError $
+                "length "
+                    ++ show len
+                    ++ " is outside 0 .. "
+                    ++ show maxLen
+    | otherwise = Right $ HKDF.expand prk labeled_info len
   where
+    maxLen = 255 * hashDigestSize (undefined :: a)
     labeled_info =
         i2ospOf_ 2 (fromIntegral len) <> "HPKE-v1" <> suite <> label <> info
 
@@ -67,5 +85,7 @@
     eae_prk :: PRK h
     eae_prk = labeledExtract suite "" "eae_prk" $ convert dh
     siz = hashDigestSize h
+    -- the hash's own digest size, so the length is in range by construction
     shared_secret =
-        labeledExpand suite eae_prk "shared_secret" kem_context siz
+        either (const "") id $
+            labeledExpand suite eae_prk "shared_secret" kem_context siz
diff --git a/Crypto/HPKE/KeySchedule.hs b/Crypto/HPKE/KeySchedule.hs
--- a/Crypto/HPKE/KeySchedule.hs
+++ b/Crypto/HPKE/KeySchedule.hs
@@ -47,7 +47,11 @@
     -> PSK_ID
     -> SharedSecret
     -> Either HPKEError (Key, Nonce, Int, PRK h)
-keySchedule h suite nk nn mode info psk psk_id shared_secret =
+keySchedule h suite nk nn mode info psk psk_id shared_secret = do
+    key <- labeledExpand suite secret "key" key_schedule_context nk
+    base_nonce <- labeledExpand suite secret "base_nonce" key_schedule_context nn
+    exporter_secret <-
+        labeledExpand suite secret "exp" key_schedule_context $ hashDigestSize h
     case toPRK exporter_secret of
         Nothing -> Left $ KeyScheduleError "cannot convert to PRK"
         Just prk -> Right (key, base_nonce, 0, prk)
@@ -60,7 +64,4 @@
 
     secret = labeledExtract suite (convert shared_secret) "secret" psk :: PRK h
 
-    key = labeledExpand suite secret "key" key_schedule_context nk
-    base_nonce = labeledExpand suite secret "base_nonce" key_schedule_context nn
 
-    exporter_secret = labeledExpand suite secret "exp" key_schedule_context $ hashDigestSize h
diff --git a/Crypto/HPKE/Types.hs b/Crypto/HPKE/Types.hs
--- a/Crypto/HPKE/Types.hs
+++ b/Crypto/HPKE/Types.hs
@@ -66,6 +66,9 @@
     | DeriveKeyPairError String
     | -- | Original
       KeyScheduleError String
+    | -- | Original.  An export longer than the @255 * Nh@ that RFC 9180
+      -- section 5.3 allows.
+      ExportError String
     | Unsupported String
     deriving (Eq, Show)
 
diff --git a/hpke.cabal b/hpke.cabal
--- a/hpke.cabal
+++ b/hpke.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               hpke
-version:            0.1.0
+version:            0.2.0
 license:            BSD3
 license-file:       LICENSE
 maintainer:         kazu@iij.ad.jp
@@ -32,7 +32,7 @@
         base >=4.7 && <5,
         base16-bytestring,
         bytestring,
-        crypton >= 1.1.0 && <1.2,
+        crypton >= 1.1.0 && <2.1,
         ram
 
     default-extensions: Strict StrictData
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -65,10 +65,19 @@
     pt1 <- open ctxR aad1 ct1
     pt1 `shouldBe` pt
 
-    exportS ctxS exporter_context0 32 `shouldBe` sec0
-    exportR ctxR exporter_context0 32 `shouldBe` sec0
-    exportS ctxS exporter_context1 32 `shouldBe` sec1
-    exportS ctxS exporter_context2 32 `shouldBe` sec2
+    exportS ctxS exporter_context0 32 `shouldBe` Right sec0
+    exportR ctxR exporter_context0 32 `shouldBe` Right sec0
+    exportS ctxS exporter_context1 32 `shouldBe` Right sec1
+    exportS ctxS exporter_context2 32 `shouldBe` Right sec2
+
+    -- RFC 9180 section 5.3 allows at most 255*Nh octets, which is 16320
+    -- even for SHA-512, the widest hash here
+    case exportS ctxS exporter_context0 20000 of
+        Left (ExportError _) -> return ()
+        r -> expectationFailure $ "an over-long export was not refused: " ++ show (fmap (const ()) r)
+    case exportR ctxR exporter_context0 20000 of
+        Left (ExportError _) -> return ()
+        r -> expectationFailure $ "an over-long export was not refused: " ++ show (fmap (const ()) r)
   where
     info = B16.decodeLenient _info
     pkEm = EncodedPublicKey $ B16.decodeLenient _pkEm
