diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # Revision history for irc-core
 
+## 2.7.0
+
+* Fix encoding when final argument is empty
+* Add `Wallops` command
+
 ## 2.6.0
 
 * Process CAP commands in library instead of client
diff --git a/irc-core.cabal b/irc-core.cabal
--- a/irc-core.cabal
+++ b/irc-core.cabal
@@ -1,5 +1,5 @@
 name:                irc-core
-version:             2.6.0
+version:             2.7.0
 synopsis:            IRC core library for glirc
 description:         IRC core library for glirc
                      .
diff --git a/src/Irc/Commands.hs b/src/Irc/Commands.hs
--- a/src/Irc/Commands.hs
+++ b/src/Irc/Commands.hs
@@ -14,8 +14,10 @@
   , ircCapEnd
   , ircCapLs
   , ircCapReq
+  , ircChantrace
   , ircCnotice
   , ircCprivmsg
+  , ircEtrace
   , ircInfo
   , ircInvite
   , ircIson
@@ -28,6 +30,7 @@
   , ircList
   , ircLusers
   , ircMap
+  , ircMasktrace
   , ircMode
   , ircMotd
   , ircNick
@@ -217,12 +220,31 @@
   RawIrcMsg
 ircTestline mask = rawIrcMsg "TESTLINE" [mask]
 
--- | TESTLINE command
+-- | TESTMASK command
 ircTestmask ::
   Text {- ^ mask  -} ->
   Text {- ^ gecos -} ->
   RawIrcMsg
 ircTestmask mask gecos = rawIrcMsg "TESTMASK" (mask : nonempty gecos)
+
+-- | MASKTRACE command
+ircMasktrace ::
+  Text {- ^ mask  -} ->
+  Text {- ^ gecos -} ->
+  RawIrcMsg
+ircMasktrace mask gecos = rawIrcMsg "MASKTRACE" [mask, gecos]
+
+-- | CHANTRACE command
+ircChantrace ::
+  Text {- ^ channel -} ->
+  RawIrcMsg
+ircChantrace channel = rawIrcMsg "CHANTRACE" [channel]
+
+-- | ETRACE command
+ircEtrace ::
+  Text {- ^ argument -} ->
+  RawIrcMsg
+ircEtrace arg = rawIrcMsg "ETRACE" [arg]
 
 -- | REMOVE command
 ircRemove ::
diff --git a/src/Irc/Message.hs b/src/Irc/Message.hs
--- a/src/Irc/Message.hs
+++ b/src/Irc/Message.hs
@@ -68,6 +68,7 @@
   | BatchEnd !Text -- ^ reference-id
   | Account !UserInfo !Text -- ^ user account name changed (account-notify extension)
   | Chghost !UserInfo !Text !Text -- ^ Target, new username and new hostname
+  | Wallops  !UserInfo !Text -- ^ Braodcast message: Source, message
   deriving Show
 
 data CapMore = CapMore | CapDone
@@ -129,11 +130,11 @@
     "JOIN" | Just user <- view msgPrefix msg
            , chan:rest <- view msgParams msg ->
 
-           let acct = case rest of
-                 ["*" , _real] -> ""
-                 [acct, _real] -> acct
-                 _             -> ""
-           in Join user (mkId chan) acct
+              Join user (mkId chan)
+            $ case rest of
+                ["*" , _real] -> ""
+                [acct, _real] -> acct
+                _             -> ""
 
     "QUIT" | Just user <- view msgPrefix msg
            , reasons   <- view msgParams msg ->
@@ -178,6 +179,10 @@
               , [newuser, newhost] <- view msgParams msg ->
       Chghost user newuser newhost
 
+    "WALLOPS" | Just user <- view msgPrefix msg
+              , [txt] <- view msgParams msg ->
+      Wallops user txt
+
     _      -> UnknownMsg msg
 
 -- | Parse a CTCP encoded message:
@@ -227,13 +232,12 @@
     BatchEnd{}               -> TargetHidden
     Account user _           -> TargetUser (userNick user)
     Chghost user _ _         -> TargetUser (userNick user)
+    Wallops src _            -> TargetWindow (userNick src)
   where
     directed src tgt
       | Text.null (userHost src) = TargetNetwork -- server message
       | tgt == me = TargetWindow (userNick src)
       | otherwise = TargetWindow tgt
-      where
-        src' = userNick src
 
     replyTarget RPL_TOPIC    (_:chan:_)   = TargetWindow (mkId chan)
     replyTarget RPL_INVITING (_:_:chan:_) = TargetWindow (mkId chan)
@@ -264,6 +268,8 @@
     Cap{}         -> Nothing
     BatchStart{}  -> Nothing
     BatchEnd{}    -> Nothing
+    Chghost x _ _ -> Just x
+    Wallops x _   -> Just x
 
 -- | Text representation of an IRC message to be used for matching with
 -- regular expressions.
@@ -291,6 +297,8 @@
     Authenticate{} -> ""
     BatchStart{}   -> ""
     BatchEnd{}     -> ""
+    Chghost x a b  -> Text.unwords [renderUserInfo x, a, b]
+    Wallops x t    -> Text.unwords [renderUserInfo x, t]
 
 capCmdText :: CapCmd -> Text
 capCmdText cmd =
diff --git a/src/Irc/RateLimit.hs b/src/Irc/RateLimit.hs
--- a/src/Irc/RateLimit.hs
+++ b/src/Irc/RateLimit.hs
@@ -39,19 +39,13 @@
   Rational {- ^ threshold seconds -} ->
   IO RateLimit
 newRateLimit penalty threshold =
-  do unless (penalty > 0)
-        (fail "newRateLimit: Penalty too small")
-
-     unless (threshold > 0)
-        (fail "newRateLimit: Threshold too small")
-
-     now <- getCurrentTime
+  do now <- getCurrentTime
      ref <- newMVar now
 
      return RateLimit
         { rateStamp     = ref
-        , rateThreshold = realToFrac threshold
-        , ratePenalty   = realToFrac penalty
+        , rateThreshold = realToFrac (max 0 threshold)
+        , ratePenalty   = realToFrac (max 0 penalty)
         }
 
 -- | Account for an event in the context of a 'RateLimit'. This command
diff --git a/src/Irc/RawIrcMsg.hs b/src/Irc/RawIrcMsg.hs
--- a/src/Irc/RawIrcMsg.hs
+++ b/src/Irc/RawIrcMsg.hs
@@ -260,7 +260,7 @@
 -- a colon or contains a space.
 buildParams :: [Text] -> Builder
 buildParams [x]
-  | " " `Text.isInfixOf` x || ":" `Text.isPrefixOf` x
+  | " " `Text.isInfixOf` x || ":" `Text.isPrefixOf` x || Text.null x
   = Builder.char8 ' ' <> Builder.char8 ':' <> Text.encodeUtf8Builder x
 buildParams (x:xs)
   = Builder.char8 ' ' <> Text.encodeUtf8Builder x <> buildParams xs
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -214,6 +214,14 @@
             { _msgTags = [TagEntry "time" "",
                           TagEntry "magic" ""] })
 
+  , assertEqual "empty last argument 1"
+      "CMD :\r\n"
+      (renderRawIrcMsg (rawIrcMsg "CMD" [""]))
+
+  , assertEqual "empty last argument 2"
+      "CMD X :\r\n"
+      (renderRawIrcMsg (rawIrcMsg "CMD" ["X", ""]))
+
   ]
 
 badRawMsgs :: Test
