diff --git a/Build/Version.hs b/Build/Version.hs
--- a/Build/Version.hs
+++ b/Build/Version.hs
@@ -5,7 +5,6 @@
 
 module Build.Version where
 
-import Data.List
 import System.Environment
 import Data.Char
 
diff --git a/BuildFlags.hs b/BuildFlags.hs
--- a/BuildFlags.hs
+++ b/BuildFlags.hs
@@ -52,11 +52,6 @@
 #ifdef WITH_MAGICMIME
 	, "MagicMime"
 #endif
-#ifdef WITH_SERVANT
-	, "Servant"
-#else
-#warning Building without servant, will not support annex+http urls or git-annex p2phttp.
-#endif
 #ifdef WITH_BENCHMARK
 	, "Benchmark"
 #endif
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,14 @@
+git-annex (10.20250929) upstream; urgency=medium
+
+  * enableremote: Allow type= to be provided when it does not change the
+    type of the special remote.
+  * importfeed: Fix encoding issues parsing feeds when built with OsPath.
+  * Fix build with ghc 9.0.2.
+  * Remove the Servant build flag; always build with support for
+    annex+http urls and git-annex p2phttp.
+
+ -- Joey Hess <id@joeyh.name>  Mon, 29 Sep 2025 11:28:09 -0400
+
 git-annex (10.20250925) upstream; urgency=medium
 
   * Fix bug that made changes to a special remote sometimes be missed when
diff --git a/CmdLine/GitAnnex.hs b/CmdLine/GitAnnex.hs
--- a/CmdLine/GitAnnex.hs
+++ b/CmdLine/GitAnnex.hs
@@ -118,9 +118,7 @@
 import qualified Command.Forget
 import qualified Command.OldKeys
 import qualified Command.P2P
-#ifdef WITH_SERVANT
 import qualified Command.P2PHttp
-#endif
 import qualified Command.Proxy
 import qualified Command.DiffDriver
 import qualified Command.Smudge
@@ -253,9 +251,7 @@
 	, Command.Forget.cmd
 	, Command.OldKeys.cmd
 	, Command.P2P.cmd
-#ifdef WITH_SERVANT
 	, Command.P2PHttp.cmd
-#endif
 	, Command.Proxy.cmd
 	, Command.DiffDriver.cmd
 	, Command.Smudge.cmd
diff --git a/Command/EnableRemote.hs b/Command/EnableRemote.hs
--- a/Command/EnableRemote.hs
+++ b/Command/EnableRemote.hs
@@ -59,11 +59,9 @@
 start o (name:rest) = go =<< filter matchingname <$> Annex.getGitRemotes
   where
 	matchingname r = Git.remoteName r == Just name
-	go [] = deadLast name $
-		let config = Logs.Remote.keyValToConfig Proposed rest
-		in case M.lookup SpecialRemote.typeField config of
-			Nothing -> startSpecialRemote o name config
-			Just _ -> giveup "Cannot change type= of existing special remote. Instead, use: git-annex initremote --sameas"
+	go [] = deadLast name $ 
+		startSpecialRemote o name $
+			Logs.Remote.keyValToConfig Proposed rest
 	go (r:_)
 		| not (null rest) = go []
 		| otherwise = do
@@ -105,6 +103,10 @@
 	starting cname ai si $ do
 		let fullconfig = config `M.union` c	
 		t <- either giveup return (SpecialRemote.findType fullconfig)
+		case SpecialRemote.findType c of
+			Right t' | t' /= t ->
+				giveup "Cannot change type= of existing special remote. Instead, use: git-annex initremote --sameas"
+			_ -> noop
 		gc <- maybe (liftIO dummyRemoteGitConfig) 
 			(return . Remote.gitconfig)
 			=<< Remote.byUUID u
diff --git a/Command/ImportFeed.hs b/Command/ImportFeed.hs
--- a/Command/ImportFeed.hs
+++ b/Command/ImportFeed.hs
@@ -645,7 +645,7 @@
 fromFeedText :: T.Text -> B.ByteString
 fromFeedText = TE.encodeUtf8
 
-{- Like Test.Feed.parseFeedFromFile, but ensures the close-on-exec bit is
+{- Like Text.Feed.parseFeedFromFile, but ensures the close-on-exec bit is
  - set when opening the file. -}
 parseFeedFromFile' :: OsPath -> IO (Maybe Feed)
 parseFeedFromFile' fp = parseFeedString <$> utf8readfile fp
diff --git a/P2P/Http/Client.hs b/P2P/Http/Client.hs
--- a/P2P/Http/Client.hs
+++ b/P2P/Http/Client.hs
@@ -27,7 +27,6 @@
 import Types.Remote
 import Annex.Common
 import qualified Git
-#ifdef WITH_SERVANT
 import qualified Annex
 import Annex.UUID
 import Annex.Url
@@ -51,12 +50,10 @@
 import Control.Concurrent.Async
 import Control.Concurrent
 import System.IO.Unsafe
-#endif
 import Data.Time.Clock.POSIX
 import qualified Data.ByteString.Lazy as L
 
 type ClientAction a
-#ifdef WITH_SERVANT
 	= ClientEnv
 	-> ProtocolVersion
 	-> B64UUID ServerSide
@@ -64,9 +61,6 @@
 	-> [B64UUID Bypass]
 	-> Maybe Auth
 	-> Annex (Either ClientError a)
-#else
-	= ()
-#endif
 
 p2pHttpClient
 	:: Remote
@@ -95,7 +89,6 @@
 	-> (String -> Annex a)
 	-> ClientAction a
 	-> Annex (Maybe a)
-#ifdef WITH_SERVANT
 p2pHttpClientVersions' allowedversion rmt rmtrepo fallback clientaction =
 	case p2pHttpBaseUrl <$> remoteAnnexP2PHttpUrl (gitconfig rmt) of
 		Nothing -> error "internal"
@@ -174,10 +167,6 @@
 				putTMVar ccv $ Git.CredentialCache $
 					M.insert (Git.CredentialBaseURL credentialbaseurl) cred cc
 		Nothing -> noop
-#else
-p2pHttpClientVersions' _ _ _ fallback () = Just <$> fallback
-	"This remote uses an annex+http url, but this version of git-annex is not built with support for that."
-#endif
 
 clientGet
 	:: Key
@@ -188,7 +177,6 @@
 	-> Maybe FileSize
 	-- ^ Size of existing file, when resuming.
 	-> ClientAction Validity
-#ifdef WITH_SERVANT
 clientGet k af consumer startsz clientenv (ProtocolVersion ver) su cu bypass auth = liftIO $ do
 	let offset = fmap (Offset . fromIntegral) startsz
 	withClientM (cli (B64Key k) cu bypass baf offset auth) clientenv $ \case
@@ -220,12 +208,8 @@
 	gather' (S.Yield v s) = LI.Chunk v <$> unsafeInterleaveIO (gather' s)
 	
 	baf = associatedFileToB64FilePath af
-#else
-clientGet _ _ _ _ = ()
-#endif
 
 clientCheckPresent :: Key -> ClientAction Bool
-#ifdef WITH_SERVANT
 clientCheckPresent key clientenv (ProtocolVersion ver) su cu bypass auth =
 	liftIO $ withClientM (cli su (B64Key key) cu bypass auth) clientenv $ \case
 		Left err -> return (Left err)
@@ -241,9 +225,6 @@
 	
 	_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI
-#else
-clientCheckPresent _ = ()
-#endif
 
 -- Similar to P2P.Protocol.remove.
 clientRemoveWithProof
@@ -275,7 +256,6 @@
 	useversion v = v >= ProtocolVersion 3
 
 clientRemove :: Key -> ClientAction RemoveResultPlus
-#ifdef WITH_SERVANT
 clientRemove k clientenv (ProtocolVersion ver) su cu bypass auth =
 	liftIO $ withClientM cli clientenv return
   where
@@ -292,15 +272,11 @@
 	_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI
-#else
-clientRemove _ = ()
-#endif
 
 clientRemoveBefore
 	:: Key
 	-> Timestamp
 	-> ClientAction RemoveResultPlus
-#ifdef WITH_SERVANT
 clientRemoveBefore k ts clientenv (ProtocolVersion ver) su cu bypass auth =
 	liftIO $ withClientM (cli su (B64Key k) cu bypass ts auth) clientenv return
   where
@@ -313,12 +289,8 @@
 		_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		_ :<|>_ :<|> _ :<|> _ :<|> _ :<|>
 		v4 :<|> v3 :<|> _ = client p2pHttpAPI
-#else
-clientRemoveBefore _ _ = ()
-#endif
 
 clientGetTimestamp :: ClientAction GetTimestampResult
-#ifdef WITH_SERVANT
 clientGetTimestamp clientenv (ProtocolVersion ver) su cu bypass auth = 
 	liftIO $ withClientM (cli su cu bypass auth) clientenv return
   where
@@ -332,9 +304,6 @@
 		_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		_ :<|> _ :<|>
 		v4 :<|> v3 :<|> _ = client p2pHttpAPI
-#else
-clientGetTimestamp = ()
-#endif
 
 clientPut
 	:: MeterUpdate
@@ -349,7 +318,6 @@
 	-- ^ Set data-present parameter and do not actually send data
 	-- (v4+ only)
 	-> ClientAction PutResultPlus
-#ifdef WITH_SERVANT
 clientPut meterupdate k moffset af contentfile contentfilesize validitycheck datapresent clientenv (ProtocolVersion ver) su cu bypass auth
 	| datapresent = liftIO $ withClientM (cli mempty) clientenv return
 	| otherwise = do
@@ -435,14 +403,10 @@
 		_ :<|> _ :<|>
 		_ :<|> _ :<|>
 		v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI
-#else
-clientPut _ _ _ _ _ _ _ _ = ()
-#endif
 
 clientPutOffset
 	:: Key
 	-> ClientAction PutOffsetResultPlus
-#ifdef WITH_SERVANT
 clientPutOffset k clientenv (ProtocolVersion ver) su cu bypass auth
 	| ver == 0 = return (Right (PutOffsetResultPlus (Offset 0)))
 	| otherwise = liftIO $ withClientM cli clientenv return
@@ -463,14 +427,10 @@
 		_ :<|> _ :<|>
 		_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		v4 :<|> v3 :<|> v2 :<|> v1 :<|> _ = client p2pHttpAPI
-#else
-clientPutOffset _ = ()
-#endif
 
 clientLockContent
 	:: Key
 	-> ClientAction LockResult
-#ifdef WITH_SERVANT
 clientLockContent k clientenv (ProtocolVersion ver) su cu bypass auth = 
 	liftIO $ withClientM (cli (B64Key k) cu bypass auth) clientenv return
   where
@@ -490,9 +450,6 @@
 		_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		_ :<|> _ :<|> _ :<|> _ :<|>
 		v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI
-#else
-clientLockContent _ = ()
-#endif
 
 clientKeepLocked
 	:: LockID
@@ -503,7 +460,6 @@
 	-- server. The lock will remain held until the callback returns,
 	-- and then will be dropped.
 	-> ClientAction a
-#ifdef WITH_SERVANT
 clientKeepLocked lckid remoteuuid unablelock callback clientenv (ProtocolVersion ver) su cu bypass auth = do
 	readyv <- liftIO newEmptyTMVarIO
 	keeplocked <- liftIO newEmptyTMVarIO
@@ -563,6 +519,3 @@
 		_ :<|> _ :<|> _ :<|> _ :<|>
 		_ :<|> _ :<|> _ :<|> _ :<|> _ :<|>
 		v4 :<|> v3 :<|> v2 :<|> v1 :<|> v0 :<|> _ = client p2pHttpAPI
-#else
-clientKeepLocked _ _ _ _ = ()
-#endif
diff --git a/P2P/Http/Types.hs b/P2P/Http/Types.hs
--- a/P2P/Http/Types.hs
+++ b/P2P/Http/Types.hs
@@ -19,11 +19,9 @@
 import qualified P2P.Protocol as P2P
 import Utility.MonotonicClock
 
-#ifdef WITH_SERVANT
 import Servant
 import Data.Aeson hiding (Key)
 import Text.Read (readMaybe)
-#endif
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
 import qualified Data.ByteString as B
@@ -149,8 +147,6 @@
 data Auth = Auth B.ByteString B.ByteString
 	deriving (Show, Generic, NFData, Eq, Ord)
 
-#ifdef WITH_SERVANT
-
 instance ToHttpApiData Auth where
 	toHeader (Auth u p) = "Basic " <> B64.encode (u <> ":" <> p)
 #if MIN_VERSION_text(2,0,0)
@@ -402,5 +398,3 @@
 	dePlus (PutOffsetResultAlreadyHavePlus _) = PutOffsetResultAlreadyHave
 	plus (PutOffsetResult o) = PutOffsetResultPlus o
 	plus PutOffsetResultAlreadyHave = PutOffsetResultAlreadyHavePlus []
-
-#endif
diff --git a/P2P/Http/Url.hs b/P2P/Http/Url.hs
--- a/P2P/Http/Url.hs
+++ b/P2P/Http/Url.hs
@@ -11,14 +11,12 @@
 
 import Data.List
 import Network.URI
-#ifdef WITH_SERVANT
 import System.FilePath.Posix as P
 import Servant.Client (BaseUrl(..), Scheme(..))
 import Text.Read
 import Data.Char
 import qualified Git
 import qualified Git.Url
-#endif
 
 defaultP2PHttpProtocolPort :: Int
 defaultP2PHttpProtocolPort = 9417 -- Git protocol is 9418
@@ -30,9 +28,7 @@
 
 data P2PHttpUrl = P2PHttpUrl
 	{ p2pHttpUrlString :: String
-#ifdef WITH_SERVANT
 	, p2pHttpBaseUrl :: BaseUrl
-#endif
 	}
 	deriving (Show)
 
@@ -40,21 +36,15 @@
 parseP2PHttpUrl us
 	| isP2PHttpProtocolUrl us = case parseURI (drop prefixlen us) of
 		Nothing -> Nothing
-#ifdef WITH_SERVANT
 		Just u ->
 			case uriScheme u of
 				"http:" -> mkbaseurl Http u
 				"https:" -> mkbaseurl Https u
 				_ -> Nothing
-#else
-		Just _u ->
-			Just $ P2PHttpUrl us
-#endif
 	| otherwise = Nothing
   where
 	prefixlen = length "annex+"
 
-#ifdef WITH_SERVANT
 	mkbaseurl s u = do
 		auth <- uriAuthority u
 		port <- if null (uriPort auth)
@@ -75,15 +65,11 @@
 	basepath u = case reverse $ P.splitDirectories (uriPath u) of
 		("git-annex":"/":rest) -> P.joinPath (reverse rest)
 		rest -> P.joinPath (reverse rest)
-#endif
 
 unavailableP2PHttpUrl :: P2PHttpUrl -> P2PHttpUrl
 unavailableP2PHttpUrl p = p
-#ifdef WITH_SERVANT
 	{ p2pHttpBaseUrl = (p2pHttpBaseUrl p) { baseUrlHost = "!dne!" } }
-#endif
 
-#ifdef WITH_SERVANT
 -- When a p2phttp url is on the same host as a git repo, which also uses
 -- http, the same username+password is assumed to be used for both.
 isP2PHttpSameHost :: P2PHttpUrl -> Git.Repo -> Bool
@@ -93,4 +79,3 @@
 		Just (map toLower $ baseUrlHost (p2pHttpBaseUrl u)) 
 			==
 		(map toLower <$> (Git.Url.host repo))
-#endif
diff --git a/Test.hs b/Test.hs
--- a/Test.hs
+++ b/Test.hs
@@ -1918,19 +1918,19 @@
   where
 	testscheme scheme = intmpclonerepo $ test_with_gpg $ \gpgcmd environ -> do
 		createDirectory (literalOsPath "dir")
-		let ps =
+		let initps =
 			[ "foo"
+			, "type=directory"
 			, "encryption=" ++ scheme
 			, "directory=dir"
 			, "highRandomQuality=false"
 			] ++ if scheme `elem` ["hybrid","pubkey"]
 				then ["keyid=" ++ Utility.Gpg.testKeyId]
 				else []
-		let initps = ps ++ [ "type=directory" ]
 		git_annex' "initremote" initps (Just environ) "initremote"
 		git_annex_shouldfail' "initremote" initps (Just environ) "initremote should not work when run twice in a row"
-		git_annex' "enableremote" ps (Just environ) "enableremote"
-		git_annex' "enableremote" ps (Just environ) "enableremote when run twice in a row"
+		git_annex' "enableremote" initps (Just environ) "enableremote"
+		git_annex' "enableremote" initps (Just environ) "enableremote when run twice in a row"
 		git_annex' "get" [annexedfile] (Just environ) "get of file"
 		annexed_present annexedfile
 		git_annex' "copy" [annexedfile, "--to", "foo"] (Just environ) "copy --to encrypted remote"
diff --git a/Utility/FileIO/CloseOnExec.hs b/Utility/FileIO/CloseOnExec.hs
--- a/Utility/FileIO/CloseOnExec.hs
+++ b/Utility/FileIO/CloseOnExec.hs
@@ -3,9 +3,9 @@
  - All functions have been modified to set the close-on-exec
  - flag to True.
  -
- - Also, functions that return a Handle have been modified to
- - use the locale encoding, working around this bug:
- - https://github.com/haskell/file-io/issues/45
+ - Also, functions that return a Handle (for a non-binary file)
+ - have been modified to use the locale encoding, working around
+ - this bug: https://github.com/haskell/file-io/issues/45
  -
  - Copyright 2025 Joey Hess <id@joeyh.name>
  - Copyright 2024 Julian Ospald
@@ -70,12 +70,12 @@
 
 withBinaryFile :: OsPath -> IOMode -> (Handle -> IO r) -> IO r
 withBinaryFile osfp iomode act = (augmentError "withBinaryFile" osfp
-    $ withOpenFileEncoding osfp iomode True False closeOnExec (try . act) True)
+    $ withOpenFile' osfp iomode True False closeOnExec (try . act) True)
   >>= either ioError pure
 
 openBinaryFile :: OsPath -> IOMode -> IO Handle
 openBinaryFile osfp iomode = augmentError "openBinaryFile" osfp $
-	 withOpenFileEncoding osfp iomode True False closeOnExec pure False
+	 withOpenFile' osfp iomode True False closeOnExec pure False
 
 readFile :: OsPath -> IO BSL.ByteString
 readFile fp = withFileNoEncoding' fp ReadMode BSL.hGetContents
diff --git a/Utility/OpenFd.hs b/Utility/OpenFd.hs
--- a/Utility/OpenFd.hs
+++ b/Utility/OpenFd.hs
@@ -14,6 +14,9 @@
 
 import System.Posix.IO.ByteString
 import System.Posix.Types
+#if ! MIN_VERSION_unix(2,8,0)
+import Control.Monad
+#endif
 
 import Utility.RawFilePath
 
diff --git a/git-annex.cabal b/git-annex.cabal
--- a/git-annex.cabal
+++ b/git-annex.cabal
@@ -1,5 +1,5 @@
 Name: git-annex
-Version: 10.20250925
+Version: 10.20250929
 Cabal-Version: 1.12
 License: AGPL-3
 Maintainer: Joey Hess <id@joeyh.name>
@@ -169,9 +169,6 @@
 Flag MagicMime
   Description: Use libmagic to determine file MIME types
 
-Flag Servant
-  Description: Use the servant library, enabling using annex+http urls and git-annex p2phttp
-
 Flag OsPath
   Description: Use the os-string library and related libraries, for faster filename manipulation
   Default: True
@@ -282,7 +279,13 @@
    network-bsd,
    git-lfs (>= 1.2.0),
    clock (>= 0.3.0),
-   crypton
+   crypton,
+   servant,
+   servant-server,
+   servant-client,
+   servant-client-core,
+   warp (>= 3.2.8),
+   warp-tls (>= 3.2.2)
   CC-Options: -Wall
   GHC-Options: -Wall -fno-warn-tabs  -Wincomplete-uni-patterns
   Default-Language: Haskell2010
@@ -309,21 +312,6 @@
   if os(linux) || os(freebsd)
     GHC-Options: -optl-Wl,--as-needed
 
-  if flag(Servant)
-    Build-Depends:
-      servant,
-      servant-server,
-      servant-client,
-      servant-client-core,
-      warp (>= 3.2.8),
-      warp-tls (>= 3.2.2)
-    CPP-Options: -DWITH_SERVANT
-    Other-Modules:
-      Command.P2PHttp
-      P2P.Http
-      P2P.Http.Server
-      P2P.Http.State
-
   if flag(OsPath)
     Build-Depends:
       os-string (>= 2.0.0),
@@ -353,8 +341,6 @@
       yesod-form (>= 1.4.8),
       yesod-core (>= 1.6.0),
       path-pieces (>= 0.2.1),
-      warp (>= 3.2.8),
-      warp-tls (>= 3.2.2),
       wai,
       wai-extra,
       blaze-builder,
@@ -718,6 +704,7 @@
     Command.MinCopies
     Command.OldKeys
     Command.P2P
+    Command.P2PHttp
     Command.P2PStdIO
     Command.PostReceive
     Command.PreCommit
@@ -917,6 +904,9 @@
     P2P.Annex
     P2P.Auth
     P2P.Generic
+    P2P.Http
+    P2P.Http.Server
+    P2P.Http.State
     P2P.Http.Types
     P2P.Http.Client
     P2P.Http.Url
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -9,7 +9,6 @@
     dbus: false
     debuglocks: false
     benchmark: true
-    servant: true
     ospath: true
 packages:
 - '.'
