diff --git a/Annex/AdjustedBranch.hs b/Annex/AdjustedBranch.hs
--- a/Annex/AdjustedBranch.hs
+++ b/Annex/AdjustedBranch.hs
@@ -596,7 +596,7 @@
 				aps <- fmap commitParent <$> findAdjustingCommit (AdjBranch currbranch)
 				case aps of
 					Just [p] -> setBasisBranch basis p
-					_ -> error $ "Unable to clean up from clone of adjusted branch; perhaps you should check out " ++ Git.Ref.describe origbranch
+					_ -> giveup $ "Unable to clean up from clone of adjusted branch; perhaps you should check out " ++ Git.Ref.describe origbranch
 			ifM versionSupportsUnlockedPointers
 				( return InAdjustedClone
 				, return NeedUpgradeForAdjustedClone
@@ -610,6 +610,6 @@
 checkVersionSupported :: Annex ()
 checkVersionSupported = do
 	unlessM versionSupportsAdjustedBranch $
-		error "Adjusted branches are only supported in v6 or newer repositories."
+		giveup "Adjusted branches are only supported in v6 or newer repositories."
 	unlessM (liftIO isGitVersionSupported) $
-		error "Your version of git is too old; upgrade it to 2.2.0 or newer to use adjusted branches."
+		giveup "Your version of git is too old; upgrade it to 2.2.0 or newer to use adjusted branches."
diff --git a/Annex/Branch.hs b/Annex/Branch.hs
--- a/Annex/Branch.hs
+++ b/Annex/Branch.hs
@@ -225,7 +225,7 @@
 	-- This check avoids some ugly error messages when the reflog
 	-- is empty.
 	ifM (null <$> inRepo (Git.RefLog.get' [Param (fromRef fullname), Param "-n1"]))
-		( error ("No reflog for " ++ fromRef fullname)
+		( giveup ("No reflog for " ++ fromRef fullname)
 		, getRef (Git.Ref.dateRef fullname date) file
 		)
 
@@ -574,7 +574,7 @@
 		<$> catFile ref differenceLog
 	mydiffs <- annexDifferences <$> Annex.getGitConfig
 	when (theirdiffs /= mydiffs) $
-		error "Remote repository is tuned in incompatable way; cannot be merged with local repository."
+		giveup "Remote repository is tuned in incompatable way; cannot be merged with local repository."
 
 ignoreRefs :: [Git.Sha] -> Annex ()
 ignoreRefs rs = do
diff --git a/Annex/Content.hs b/Annex/Content.hs
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -268,8 +268,8 @@
 		(unlock lockfile)
 		(const a)
   where
-	alreadylocked = error "content is locked"
-	failedtolock e = error $ "failed to lock content: " ++ show e
+	alreadylocked = giveup "content is locked"
+	failedtolock e = giveup $ "failed to lock content: " ++ show e
 
 	lock contentfile lockfile =
 		(maybe alreadylocked return 
diff --git a/Annex/FileMatcher.hs b/Annex/FileMatcher.hs
--- a/Annex/FileMatcher.hs
+++ b/Annex/FileMatcher.hs
@@ -165,7 +165,7 @@
 	mkmatcher expr = do
 		parser <- mkLargeFilesParser
 		either badexpr return $ parsedToMatcher $ parser expr
-	badexpr e = error $ "bad annex.largefiles configuration: " ++ e
+	badexpr e = giveup $ "bad annex.largefiles configuration: " ++ e
 
 simply :: MatchFiles Annex -> ParseResult
 simply = Right . Operation
diff --git a/Annex/Init.hs b/Annex/Init.hs
--- a/Annex/Init.hs
+++ b/Annex/Init.hs
@@ -129,7 +129,7 @@
   where
 	needsinit = ifM Annex.Branch.hasSibling
 			( initialize Nothing Nothing
-			, error "First run: git-annex init"
+			, giveup "First run: git-annex init"
 			)
 
 {- Checks if a repository is initialized. Does not check version for ugrade. -}
diff --git a/Annex/View.hs b/Annex/View.hs
--- a/Annex/View.hs
+++ b/Annex/View.hs
@@ -110,7 +110,7 @@
 			in (view', Narrowing)
 	
 	checksize r@(v, _)
-		| viewTooLarge v = error $ "View is too large (" ++ show (visibleViewSize v) ++ " levels of subdirectories)"
+		| viewTooLarge v = giveup $ "View is too large (" ++ show (visibleViewSize v) ++ " levels of subdirectories)"
 		| otherwise = r
 
 updateViewComponent :: ViewComponent -> MetaField -> ViewFilter -> Writer [ViewChange] ViewComponent
@@ -424,4 +424,4 @@
 	return branch
 
 withCurrentView :: (View -> Annex a) -> Annex a
-withCurrentView a = maybe (error "Not in a view.") a =<< currentView
+withCurrentView a = maybe (giveup "Not in a view.") a =<< currentView
diff --git a/Assistant/Alert.hs b/Assistant/Alert.hs
--- a/Assistant/Alert.hs
+++ b/Assistant/Alert.hs
@@ -26,7 +26,6 @@
 import Assistant.DaemonStatus
 import Assistant.WebApp.Types
 import Assistant.WebApp (renderUrl)
-import Yesod
 #endif
 import Assistant.Monad
 import Assistant.Types.UrlRenderer
diff --git a/Assistant/Sync.hs b/Assistant/Sync.hs
--- a/Assistant/Sync.hs
+++ b/Assistant/Sync.hs
@@ -21,7 +21,6 @@
 import qualified Git
 import qualified Git.Command
 import qualified Git.Ref
-import qualified Git.Merge
 import qualified Remote
 import qualified Types.Remote as Remote
 import qualified Remote.List as Remote
@@ -239,18 +238,11 @@
 			)
 	haddiverged <- liftAnnex Annex.Branch.forceUpdate
 	forM_ normalremotes $ \r ->
-		liftAnnex $ Command.Sync.mergeRemote r currentbranch mergeConfig
+		liftAnnex $ Command.Sync.mergeRemote r currentbranch Command.Sync.mergeConfig
 	u <- liftAnnex getUUID
 	forM_ xmppremotes $ \r ->
 		sendNetMessage $ Pushing (getXMPPClientID r) (PushRequest u)
 	return (catMaybes failed, haddiverged)
-
-mergeConfig :: [Git.Merge.MergeConfig]
-mergeConfig = 
-	[ Git.Merge.MergeNonInteractive
-	-- Pairing involves merging unrelated histories
-	, Git.Merge.MergeUnrelatedHistories
-	]
 
 {- Start syncing a remote, using a background thread. -}
 syncRemote :: Remote -> Assistant ()
diff --git a/Assistant/Threads/Merger.hs b/Assistant/Threads/Merger.hs
--- a/Assistant/Threads/Merger.hs
+++ b/Assistant/Threads/Merger.hs
@@ -12,7 +12,6 @@
 import Assistant.BranchChange
 import Assistant.DaemonStatus
 import Assistant.ScanRemotes
-import Assistant.Sync
 import Utility.DirWatcher
 import Utility.DirWatcher.Types
 import qualified Annex.Branch
@@ -86,7 +85,7 @@
 					, "into", Git.fromRef b
 					]
 				void $ liftAnnex $ Command.Sync.merge
-					currbranch mergeConfig
+					currbranch Command.Sync.mergeConfig
 					Git.Branch.AutomaticCommit
 					changedbranch
 	mergecurrent _ = noop
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -65,10 +65,10 @@
 #else
 		noop
 #endif
-	| otherwise = error "watch mode is not available on this system"
+	| otherwise = giveup "watch mode is not available on this system"
 
 needLsof :: Annex ()
-needLsof = error $ unlines
+needLsof = giveup $ unlines
 	[ "The lsof command is needed for watch mode to be safe, and is not in PATH."
 	, "To override lsof checks to ensure that files are not open for writing"
 	, "when added to the annex, you can use --force"
diff --git a/Assistant/Threads/WebApp.hs b/Assistant/Threads/WebApp.hs
--- a/Assistant/Threads/WebApp.hs
+++ b/Assistant/Threads/WebApp.hs
@@ -71,7 +71,7 @@
 #ifdef __ANDROID__
 	when (isJust listenhost') $
 		-- See Utility.WebApp
-		error "Sorry, --listen is not currently supported on Android"
+		giveup "Sorry, --listen is not currently supported on Android"
 #endif
 	webapp <- WebApp
 		<$> pure assistantdata
diff --git a/Assistant/Upgrade.hs b/Assistant/Upgrade.hs
--- a/Assistant/Upgrade.hs
+++ b/Assistant/Upgrade.hs
@@ -153,7 +153,7 @@
   where
 	changeprogram program = liftIO $ do
 		unlessM (boolSystem program [Param "version"]) $
-			error "New git-annex program failed to run! Not using."
+			giveup "New git-annex program failed to run! Not using."
 		pf <- programFile
 		liftIO $ writeFile pf program
 	
diff --git a/Assistant/WebApp/Configurators/AWS.hs b/Assistant/WebApp/Configurators/AWS.hs
--- a/Assistant/WebApp/Configurators/AWS.hs
+++ b/Assistant/WebApp/Configurators/AWS.hs
@@ -139,7 +139,7 @@
 				]
 		_ -> $(widgetFile "configurators/adds3")
 #else
-postAddS3R = error "S3 not supported by this build"
+postAddS3R = giveup "S3 not supported by this build"
 #endif
 
 getAddGlacierR :: Handler Html
@@ -161,7 +161,7 @@
 				]
 		_ -> $(widgetFile "configurators/addglacier")
 #else
-postAddGlacierR = error "S3 not supported by this build"
+postAddGlacierR = giveup "S3 not supported by this build"
 #endif
 
 getEnableS3R :: UUID -> Handler Html
@@ -179,7 +179,7 @@
 #ifdef WITH_S3
 postEnableS3R uuid = awsConfigurator $ enableAWSRemote S3.remote uuid
 #else
-postEnableS3R _ = error "S3 not supported by this build"
+postEnableS3R _ = giveup "S3 not supported by this build"
 #endif
 
 getEnableGlacierR :: UUID -> Handler Html
@@ -205,7 +205,7 @@
 				T.pack <$> Remote.prettyUUID uuid
 			$(widgetFile "configurators/enableaws")
 #else
-enableAWSRemote _ _ = error "S3 not supported by this build"
+enableAWSRemote _ _ = giveup "S3 not supported by this build"
 #endif
 
 makeAWSRemote :: SpecialRemoteMaker -> RemoteType -> StandardGroup -> AWSCreds -> RemoteName -> RemoteConfig -> Handler ()
diff --git a/Assistant/WebApp/Configurators/IA.hs b/Assistant/WebApp/Configurators/IA.hs
--- a/Assistant/WebApp/Configurators/IA.hs
+++ b/Assistant/WebApp/Configurators/IA.hs
@@ -147,7 +147,7 @@
 					]
 		_ -> $(widgetFile "configurators/addia")
 #else
-postAddIAR = error "S3 not supported by this build"
+postAddIAR = giveup "S3 not supported by this build"
 #endif
 
 getEnableIAR :: UUID -> Handler Html
@@ -157,7 +157,7 @@
 #ifdef WITH_S3
 postEnableIAR = iaConfigurator . enableIARemote
 #else
-postEnableIAR _ = error "S3 not supported by this build"
+postEnableIAR _ = giveup "S3 not supported by this build"
 #endif
 
 #ifdef WITH_S3
diff --git a/Assistant/WebApp/Configurators/Local.hs b/Assistant/WebApp/Configurators/Local.hs
--- a/Assistant/WebApp/Configurators/Local.hs
+++ b/Assistant/WebApp/Configurators/Local.hs
@@ -151,7 +151,7 @@
 postFirstRepositoryR :: Handler Html
 postFirstRepositoryR = page "Getting started" (Just Configuration) $ do
 	unlessM (liftIO $ inPath "git") $
-		error "You need to install git in order to use git-annex!"
+		giveup "You need to install git in order to use git-annex!"
 #ifdef __ANDROID__
 	androidspecial <- liftIO $ doesDirectoryExist "/sdcard/DCIM"
 	let path = "/sdcard/annex"
@@ -309,7 +309,7 @@
 		mu <- liftAnnex $ probeGCryptRemoteUUID dir
 		case mu of
 			Just u -> enableexistinggcryptremote u
-			Nothing -> error "The drive contains a gcrypt repository that is not a git-annex special remote. This is not supported."
+			Nothing -> giveup "The drive contains a gcrypt repository that is not a git-annex special remote. This is not supported."
 	enableexistinggcryptremote u = do
 		remotename' <- liftAnnex $ getGCryptRemoteName u dir
 		makewith $ const $ do
diff --git a/Assistant/WebApp/Configurators/Ssh.hs b/Assistant/WebApp/Configurators/Ssh.hs
--- a/Assistant/WebApp/Configurators/Ssh.hs
+++ b/Assistant/WebApp/Configurators/Ssh.hs
@@ -196,7 +196,7 @@
 	enablegcrypt sshdata _ = prepSsh False sshdata $ \sshdata' ->
 		sshConfigurator $
 			checkExistingGCrypt sshdata' $
-				error "Expected to find an encrypted git repository, but did not."
+				giveup "Expected to find an encrypted git repository, but did not."
 	getsshinput = parseSshUrl <=< M.lookup "gitrepo"
 
 getEnableSshGitRemoteR :: UUID -> Handler Html
@@ -475,7 +475,7 @@
 	case mu of
 		Just u -> void $ liftH $
 			combineExistingGCrypt sshdata u
-		Nothing -> error "The location contains a gcrypt repository that is not a git-annex special remote. This is not supported."
+		Nothing -> giveup "The location contains a gcrypt repository that is not a git-annex special remote. This is not supported."
   where
 	repourl = genSshUrl sshdata
 
@@ -641,7 +641,7 @@
 		checkGCryptRepoEncryption (genSshUrl sshdata) notencrypted notinstalled $
 			enableGCrypt sshdata reponame
   where
-	notencrypted = error "Unexpectedly found a non-encrypted git repository, instead of the expected encrypted git repository."
+	notencrypted = giveup "Unexpectedly found a non-encrypted git repository, instead of the expected encrypted git repository."
 	notinstalled = error "internal"
 
 {- Prepares rsync.net ssh key and creates the directory that will be 
diff --git a/Assistant/WebApp/Configurators/WebDAV.hs b/Assistant/WebApp/Configurators/WebDAV.hs
--- a/Assistant/WebApp/Configurators/WebDAV.hs
+++ b/Assistant/WebApp/Configurators/WebDAV.hs
@@ -82,7 +82,7 @@
 				]
 		_ -> $(widgetFile "configurators/addbox.com")
 #else
-postAddBoxComR = error "WebDAV not supported by this build"
+postAddBoxComR = giveup "WebDAV not supported by this build"
 #endif
 
 getEnableWebDAVR :: UUID -> Handler Html
@@ -120,7 +120,7 @@
 					T.pack <$> Remote.prettyUUID uuid
 				$(widgetFile "configurators/enablewebdav")
 #else
-postEnableWebDAVR _ = error "WebDAV not supported by this build"
+postEnableWebDAVR _ = giveup "WebDAV not supported by this build"
 #endif
 
 #ifdef WITH_WEBDAV
diff --git a/Assistant/WebApp/Form.hs b/Assistant/WebApp/Form.hs
--- a/Assistant/WebApp/Form.hs
+++ b/Assistant/WebApp/Form.hs
@@ -57,7 +57,7 @@
 		in [whamlet|^{fieldwidget}&nbsp;&nbsp;<span>^{note}</span>|]
 
 {- Note that the toggle string must be unique on the form. -}
-withExpandableNote :: (Monad m, ToWidget (HandlerSite m) w) => Field m v -> (String, w) -> Field m v
+withExpandableNote :: (ToWidget (HandlerSite m) w) => Field m v -> (String, w) -> Field m v
 withExpandableNote field (toggle, note) = withNote field $ [whamlet|
 <a .btn .btn-default data-toggle="collapse" data-target="##{ident}">#{toggle}</a>
 <div ##{ident} .collapse>
diff --git a/Assistant/WebApp/Gpg.hs b/Assistant/WebApp/Gpg.hs
--- a/Assistant/WebApp/Gpg.hs
+++ b/Assistant/WebApp/Gpg.hs
@@ -56,7 +56,7 @@
 	liftIO $ genSecretKey cmd RSA "" userid maxRecommendedKeySize
 	results <- M.keys . M.filter (== userid) <$> liftIO (secretKeys cmd)
 	case results of
-		[] -> error "Failed to generate gpg key!"
+		[] -> giveup "Failed to generate gpg key!"
 		(key:_) -> use key
 
 {- Tries to find the name used in remote.log for a gcrypt repository
@@ -85,7 +85,7 @@
 	void $ inRepo $ Git.Remote.Remove.remove tmpremote
 	maybe missing return mname
   where
-	missing = error $ "Cannot find configuration for the gcrypt remote at " ++ repoloc
+	missing = giveup $ "Cannot find configuration for the gcrypt remote at " ++ repoloc
 
 {- Checks to see if a repo is encrypted with gcrypt, and runs one action if
  - it's not an another if it is.
@@ -103,7 +103,7 @@
 	dispatch Git.GCrypt.Decryptable = encrypted
 	dispatch Git.GCrypt.NotEncrypted = notencrypted
 	dispatch Git.GCrypt.NotDecryptable =
-		error "This git repository is encrypted with a GnuPG key that you do not have."
+		giveup "This git repository is encrypted with a GnuPG key that you do not have."
 
 {- Gets the UUID of the gcrypt repo at a location, which may not exist.
  - Only works if the gcrypt repo was created as a git-annex remote. -}
diff --git a/Assistant/WebApp/Types.hs b/Assistant/WebApp/Types.hs
--- a/Assistant/WebApp/Types.hs
+++ b/Assistant/WebApp/Types.hs
@@ -10,7 +10,10 @@
 {-# LANGUAGE FlexibleInstances, FlexibleContexts, ViewPatterns #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
-module Assistant.WebApp.Types where
+module Assistant.WebApp.Types (
+	module Assistant.WebApp.Types,
+	Route
+) where
 
 import Assistant.Common
 import Assistant.Ssh
@@ -48,7 +51,8 @@
 	}
 
 mkYesodData "WebApp" $(parseRoutesFile "Assistant/WebApp/routes")
-	  
+
+excludeStatic :: [Text] -> Bool
 excludeStatic [] = True
 excludeStatic (p:_) = p /= "static"
 
diff --git a/Build/LinuxMkLibs.hs b/Build/LinuxMkLibs.hs
--- a/Build/LinuxMkLibs.hs
+++ b/Build/LinuxMkLibs.hs
@@ -70,7 +70,6 @@
 			-- Assume that for a symlink, the destination
 			-- will also be shimmed.
 			let sl' = ".." </> takeFileName sl </> takeFileName sl
-			print (sl', exedest)
 			createSymbolicLink sl' exedest
 		, renameFile exe exedest
 		)
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,23 @@
+git-annex (6.20161118) unstable; urgency=medium
+
+  * git-annex.cabal: Loosen bounds on persistent to allow 2.5, which
+    on Debian has been patched to work with esqueleto.
+    This may break cabal's resolver on non-Debian systems;
+    if so, either use stack to build, or run cabal with 
+    --constraint='persistent ==2.2.4.1'
+    Hopefully this mess with esqueleto will be resolved soon.
+  * sync: Pass --allow-unrelated-histories to git merge when used with git
+    git 2.9.0 or newer. This makes merging a remote into a freshly created
+    direct mode repository work the same as it works in indirect mode.
+  * Avoid backtraces on expected failures when built with ghc 8;
+    only use backtraces for unexpected errors.
+  * fsck --all --from was checking the existence and content of files
+    in the local repository, rather than on the special remote. Oops.
+  * Linux arm standalone: Build with a 32kb page size, which is needed
+    on several ARM NAS devices, including Drobo 5N, and WD NAS.
+
+ -- Joey Hess <id@joeyh.name>  Fri, 18 Nov 2016 11:43:14 -0400
+
 git-annex (6.20161111) unstable; urgency=medium
 
   * Restarting a crashing git process could result in filename encoding
diff --git a/CmdLine/Action.hs b/CmdLine/Action.hs
--- a/CmdLine/Action.hs
+++ b/CmdLine/Action.hs
@@ -38,7 +38,7 @@
 	showerrcount =<< Annex.getState Annex.errcounter
   where
 	showerrcount 0 = noop
-	showerrcount cnt = error $ name ++ ": " ++ show cnt ++ " failed"
+	showerrcount cnt = giveup $ name ++ ": " ++ show cnt ++ " failed"
 
 {- Runs one of the actions needed to perform a command.
  - Individual actions can fail without stopping the whole command,
diff --git a/CmdLine/Batch.hs b/CmdLine/Batch.hs
--- a/CmdLine/Batch.hs
+++ b/CmdLine/Batch.hs
@@ -56,7 +56,7 @@
 			either parseerr a (parser v)
 			batchInput parser a
   where
-	parseerr s = error $ "Batch input parse failure: " ++ s
+	parseerr s = giveup $ "Batch input parse failure: " ++ s
 
 -- Runs a CommandStart in batch mode.
 --
diff --git a/CmdLine/GitAnnexShell.hs b/CmdLine/GitAnnexShell.hs
--- a/CmdLine/GitAnnexShell.hs
+++ b/CmdLine/GitAnnexShell.hs
@@ -71,7 +71,7 @@
 		check Nothing = unexpected expected "uninitialized repository"
 		check (Just u) = unexpectedUUID expected u
 	unexpectedUUID expected u = unexpected expected $ "UUID " ++ fromUUID u
-	unexpected expected s = error $
+	unexpected expected s = giveup $
 		"expected repository UUID " ++ expected ++ " but found " ++ s
 
 run :: [String] -> IO ()
@@ -109,7 +109,7 @@
 		Git.Config.read r
 			`catchIO` \_ -> do
 				hn <- fromMaybe "unknown" <$> getHostname
-				error $ "failed to read git config of git repository in " ++ hn ++ " on " ++ dir ++ "; perhaps this repository is not set up correctly or has moved"
+				giveup $ "failed to read git config of git repository in " ++ hn ++ " on " ++ dir ++ "; perhaps this repository is not set up correctly or has moved"
 
 external :: [String] -> IO ()
 external params = do
@@ -120,7 +120,7 @@
 	checkDirectory lastparam
 	checkNotLimited
 	unlessM (boolSystem "git-shell" $ map Param $ "-c":params') $
-		error "git-shell failed"
+		giveup "git-shell failed"
 
 {- Split the input list into 3 groups separated with a double dash --.
  - Parameters between two -- markers are field settings, in the form:
@@ -150,6 +150,6 @@
 	| otherwise = False
 
 failure :: IO ()
-failure = error $ "bad parameters\n\n" ++ usage h cmds
+failure = giveup $ "bad parameters\n\n" ++ usage h cmds
   where
 	h = "git-annex-shell [-c] command [parameters ...] [option ...]"
diff --git a/CmdLine/GitAnnexShell/Checks.hs b/CmdLine/GitAnnexShell/Checks.hs
--- a/CmdLine/GitAnnexShell/Checks.hs
+++ b/CmdLine/GitAnnexShell/Checks.hs
@@ -26,7 +26,7 @@
 	case v of
 		Nothing -> noop
 		Just "" -> noop
-		Just _ -> error $ "Action blocked by " ++ var
+		Just _ -> giveup $ "Action blocked by " ++ var
 
 checkDirectory :: Maybe FilePath -> IO ()
 checkDirectory mdir = do
@@ -44,7 +44,7 @@
 					then noop
 					else req d' (Just dir')
   where
-	req d mdir' = error $ unwords 
+	req d mdir' = giveup $ unwords 
 		[ "Only allowed to access"
 		, d
 		, maybe "and could not determine directory from command line" ("not " ++) mdir'
@@ -64,4 +64,4 @@
 gitAnnexShellCheck = addCheck okforshell . dontCheck repoExists
   where
 	okforshell = unlessM (isInitialized <||> isJust . gcryptId <$> Annex.getGitConfig) $
-		error "Not a git-annex or gcrypt repository."
+		giveup "Not a git-annex or gcrypt repository."
diff --git a/CmdLine/Seek.hs b/CmdLine/Seek.hs
--- a/CmdLine/Seek.hs
+++ b/CmdLine/Seek.hs
@@ -40,7 +40,7 @@
 withFilesInGitNonRecursive needforce a params = ifM (Annex.getState Annex.force)
 	( withFilesInGit a params
 	, if null params
-		then error needforce
+		then giveup needforce
 		else seekActions $ prepFiltered a (getfiles [] params)
 	)
   where
@@ -54,7 +54,7 @@
 			[] -> do
 				void $ liftIO $ cleanup
 				getfiles c ps
-			_ -> error needforce
+			_ -> giveup needforce
 
 withFilesNotInGit :: Bool -> (FilePath -> CommandStart) -> CmdParams -> CommandSeek
 withFilesNotInGit skipdotfiles a params
@@ -117,7 +117,7 @@
   where
 	pairs c [] = reverse c
 	pairs c (x:y:xs) = pairs ((x,y):c) xs
-	pairs _ _ = error "expected pairs"
+	pairs _ _ = giveup "expected pairs"
 
 withFilesToBeCommitted :: (FilePath -> CommandStart) -> CmdParams -> CommandSeek
 withFilesToBeCommitted a params = seekActions $ prepFiltered a $
@@ -152,11 +152,11 @@
 withKeys :: (Key -> CommandStart) -> CmdParams -> CommandSeek
 withKeys a params = seekActions $ return $ map (a . parse) params
   where
-	parse p = fromMaybe (error "bad key") $ file2key p
+	parse p = fromMaybe (giveup "bad key") $ file2key p
 
 withNothing :: CommandStart -> CmdParams -> CommandSeek
 withNothing a [] = seekActions $ return [a]
-withNothing _ _ = error "This command takes no parameters."
+withNothing _ _ = giveup "This command takes no parameters."
 
 {- Handles the --all, --branch, --unused, --failed, --key, and
  - --incomplete options, which specify particular keys to run an
@@ -191,7 +191,7 @@
 withKeyOptions' ko auto mkkeyaction fallbackaction params = do
 	bare <- fromRepo Git.repoIsLocalBare
 	when (auto && bare) $
-		error "Cannot use --auto in a bare repository"
+		giveup "Cannot use --auto in a bare repository"
 	case (null params, ko) of
 		(True, Nothing)
 			| bare -> noauto $ runkeyaction loggedKeys
@@ -203,10 +203,10 @@
 		(True, Just (WantSpecificKey k)) -> noauto $ runkeyaction (return [k])
 		(True, Just WantIncompleteKeys) -> noauto $ runkeyaction incompletekeys
 		(True, Just (WantBranchKeys bs)) -> noauto $ runbranchkeys bs
-		(False, Just _) -> error "Can only specify one of file names, --all, --branch, --unused, --failed, --key, or --incomplete"
+		(False, Just _) -> giveup "Can only specify one of file names, --all, --branch, --unused, --failed, --key, or --incomplete"
   where
 	noauto a
-		| auto = error "Cannot use --auto with --all or --branch or --unused or --key or --incomplete"
+		| auto = giveup "Cannot use --auto with --all or --branch or --unused or --key or --incomplete"
 		| otherwise = a
 	incompletekeys = staleKeysPrune gitAnnexTmpObjectDir True
 	runkeyaction getks = do
diff --git a/Command.hs b/Command.hs
--- a/Command.hs
+++ b/Command.hs
@@ -101,15 +101,15 @@
 
 notDirect :: Command -> Command
 notDirect = addCheck $ whenM isDirect $
-	error "You cannot run this command in a direct mode repository."
+	giveup "You cannot run this command in a direct mode repository."
 
 notBareRepo :: Command -> Command
 notBareRepo = addCheck $ whenM (fromRepo Git.repoIsLocalBare) $
-	error "You cannot run this command in a bare repository."
+	giveup "You cannot run this command in a bare repository."
 
 noDaemonRunning :: Command -> Command
 noDaemonRunning = addCheck $ whenM (isJust <$> daemonpid) $
-	error "You cannot run this command while git-annex watch or git-annex assistant is running."
+	giveup "You cannot run this command while git-annex watch or git-annex assistant is running."
   where
 	daemonpid = liftIO . checkDaemon =<< fromRepo gitAnnexPidFile
 
diff --git a/Command/AddUnused.hs b/Command/AddUnused.hs
--- a/Command/AddUnused.hs
+++ b/Command/AddUnused.hs
@@ -38,4 +38,4 @@
  - it seems better to error out, rather than moving bad/tmp content into
  - the annex. -}
 performOther :: String -> Key -> CommandPerform
-performOther other _ = error $ "cannot addunused " ++ other ++ "content"
+performOther other _ = giveup $ "cannot addunused " ++ other ++ "content"
diff --git a/Command/AddUrl.hs b/Command/AddUrl.hs
--- a/Command/AddUrl.hs
+++ b/Command/AddUrl.hs
@@ -133,7 +133,7 @@
 				let f' = adjustFile o (deffile </> fromSafeFilePath f)
 				void $ commandAction $
 					startRemote r (relaxedOption o) f' u' sz
-		| otherwise = error $ unwords
+		| otherwise = giveup $ unwords
 			[ "That url contains multiple files according to the"
 			, Remote.name r
 			, " remote; cannot add it to a single file."
@@ -182,7 +182,7 @@
 startWeb o s = go $ fromMaybe bad $ parseURI urlstring
   where
 	(urlstring, downloader) = getDownloader s
-	bad = fromMaybe (error $ "bad url " ++ urlstring) $
+	bad = fromMaybe (giveup $ "bad url " ++ urlstring) $
 		Url.parseURIRelaxed $ urlstring
 	go url = case downloader of
 		QuviDownloader -> usequvi
@@ -208,7 +208,7 @@
 						)
 		showStart "addurl" file
 		next $ performWeb (relaxedOption o) urlstring file urlinfo
-	badquvi = error $ "quvi does not know how to download url " ++ urlstring
+	badquvi = giveup $ "quvi does not know how to download url " ++ urlstring
 	usequvi = do
 		page <- fromMaybe badquvi
 			<$> withQuviOptions Quvi.forceQuery [Quvi.quiet, Quvi.httponly] urlstring
@@ -372,7 +372,7 @@
 		| depth >= length urlbits -> frombits id
 		| depth > 0 -> frombits $ drop depth
 		| depth < 0 -> frombits $ reverse . take (negate depth) . reverse
-		| otherwise -> error "bad --pathdepth"
+		| otherwise -> giveup "bad --pathdepth"
   where
 	fullurl = concat
 		[ maybe "" uriRegName (uriAuthority url)
@@ -385,7 +385,7 @@
 
 urlString2file :: URLString -> Maybe Int -> Int -> FilePath
 urlString2file s pathdepth pathmax = case Url.parseURIRelaxed s of
-	Nothing -> error $ "bad uri " ++ s
+	Nothing -> giveup $ "bad uri " ++ s
 	Just u -> url2file u pathdepth pathmax
 
 adjustFile :: AddUrlOptions -> FilePath -> FilePath
diff --git a/Command/Assistant.hs b/Command/Assistant.hs
--- a/Command/Assistant.hs
+++ b/Command/Assistant.hs
@@ -66,14 +66,14 @@
 startNoRepo o
 	| autoStartOption o = autoStart o
 	| autoStopOption o = autoStop
-	| otherwise = error "Not in a git repository."
+	| otherwise = giveup "Not in a git repository."
 
 autoStart :: AssistantOptions -> IO ()
 autoStart o = do
 	dirs <- liftIO readAutoStartFile
 	when (null dirs) $ do
 		f <- autoStartFile
-		error $ "Nothing listed in " ++ f
+		giveup $ "Nothing listed in " ++ f
 	program <- programPath
 	haveionice <- pure Build.SysConfig.ionice <&&> inPath "ionice"
 	forM_ dirs $ \d -> do
diff --git a/Command/CheckPresentKey.hs b/Command/CheckPresentKey.hs
--- a/Command/CheckPresentKey.hs
+++ b/Command/CheckPresentKey.hs
@@ -40,7 +40,7 @@
 			_ -> wrongnumparams
 		batchInput Right $ checker >=> batchResult
   where
-	wrongnumparams = error "Wrong number of parameters"
+	wrongnumparams = giveup "Wrong number of parameters"
 					
 data Result = Present | NotPresent | CheckFailure String
 
@@ -71,8 +71,8 @@
 batchResult _ = liftIO $ putStrLn "0"
 
 toKey :: String -> Key
-toKey = fromMaybe (error "Bad key") . file2key
+toKey = fromMaybe (giveup "Bad key") . file2key
 
 toRemote :: String -> Annex Remote
-toRemote rn = maybe (error "Unknown remote") return
+toRemote rn = maybe (giveup "Unknown remote") return
 	=<< Remote.byNameWithUUID (Just rn)
diff --git a/Command/ContentLocation.hs b/Command/ContentLocation.hs
--- a/Command/ContentLocation.hs
+++ b/Command/ContentLocation.hs
@@ -19,7 +19,7 @@
 
 run :: () -> String -> Annex Bool
 run _ p = do
-	let k = fromMaybe (error "bad key") $ file2key p
+	let k = fromMaybe (giveup "bad key") $ file2key p
 	maybe (return False) (\f -> liftIO (putStrLn f) >> return True)
 		=<< inAnnex' (pure True) Nothing check k
   where
diff --git a/Command/Dead.hs b/Command/Dead.hs
--- a/Command/Dead.hs
+++ b/Command/Dead.hs
@@ -37,7 +37,7 @@
 	ls <- keyLocations key
 	case ls of
 		[] -> next $ performKey key
-		_ -> error "This key is still known to be present in some locations; not marking as dead."
+		_ -> giveup "This key is still known to be present in some locations; not marking as dead."
 		
 performKey :: Key -> CommandPerform
 performKey key = do
diff --git a/Command/Describe.hs b/Command/Describe.hs
--- a/Command/Describe.hs
+++ b/Command/Describe.hs
@@ -25,7 +25,7 @@
 	showStart "describe" name
 	u <- Remote.nameToUUID name
 	next $ perform u $ unwords description
-start _ = error "Specify a repository and a description."	
+start _ = giveup "Specify a repository and a description."	
 
 perform :: UUID -> String -> CommandPerform
 perform u description = do
diff --git a/Command/DiffDriver.hs b/Command/DiffDriver.hs
--- a/Command/DiffDriver.hs
+++ b/Command/DiffDriver.hs
@@ -73,7 +73,7 @@
 	mk (unmergedpath:[]) = UnmergedReq { rPath = unmergedpath }
 	mk _ = badopts
 
-	badopts = error $ "Unexpected input: " ++ unwords opts
+	badopts = giveup $ "Unexpected input: " ++ unwords opts
 
 {- Check if either file is a symlink to a git-annex object,
  - which git-diff will leave as a normal file containing the link text.
diff --git a/Command/Direct.hs b/Command/Direct.hs
--- a/Command/Direct.hs
+++ b/Command/Direct.hs
@@ -26,7 +26,7 @@
 start :: CommandStart
 start = ifM versionSupportsDirectMode
 	( ifM isDirect ( stop , next perform )
-	, error "Direct mode is not suppported by this repository version. Use git-annex unlock instead."
+	, giveup "Direct mode is not suppported by this repository version. Use git-annex unlock instead."
 	)
 
 perform :: CommandPerform
diff --git a/Command/DropKey.hs b/Command/DropKey.hs
--- a/Command/DropKey.hs
+++ b/Command/DropKey.hs
@@ -32,7 +32,7 @@
 seek :: DropKeyOptions -> CommandSeek
 seek o = do
 	unlessM (Annex.getState Annex.force) $
-		error "dropkey can cause data loss; use --force if you're sure you want to do this"
+		giveup "dropkey can cause data loss; use --force if you're sure you want to do this"
 	withKeys start (toDrop o)
 	case batchOption o of
 		Batch -> batchInput parsekey $ batchCommandAction . start
diff --git a/Command/EnableRemote.hs b/Command/EnableRemote.hs
--- a/Command/EnableRemote.hs
+++ b/Command/EnableRemote.hs
@@ -63,7 +63,7 @@
 		_ -> unknownNameError "Unknown remote name."
 startSpecialRemote name config (Just (u, c)) = do
 	let fullconfig = config `M.union` c	
-	t <- either error return (Annex.SpecialRemote.findType fullconfig)
+	t <- either giveup return (Annex.SpecialRemote.findType fullconfig)
 	showStart "enableremote" name
 	gc <- maybe def Remote.gitconfig <$> Remote.byUUID u
 	next $ performSpecialRemote t u fullconfig gc
@@ -94,7 +94,7 @@
 	disabledremotes <- filterM isdisabled =<< Annex.fromRepo Git.remotes
 	let remotesmsg = unlines $ map ("\t" ++) $
 		mapMaybe Git.remoteName disabledremotes
-	error $ concat $ filter (not . null) [prefix ++ "\n", remotesmsg, specialmsg]
+	giveup $ concat $ filter (not . null) [prefix ++ "\n", remotesmsg, specialmsg]
   where
 	isdisabled r = anyM id
 		[ (==) NoUUID <$> getRepoUUID r
diff --git a/Command/ExamineKey.hs b/Command/ExamineKey.hs
--- a/Command/ExamineKey.hs
+++ b/Command/ExamineKey.hs
@@ -21,6 +21,6 @@
 
 run :: Maybe Utility.Format.Format -> String -> Annex Bool
 run format p = do
-	let k = fromMaybe (error "bad key") $ file2key p
+	let k = fromMaybe (giveup "bad key") $ file2key p
 	showFormatted format (key2file k) (keyVars k)
 	return True
diff --git a/Command/Expire.hs b/Command/Expire.hs
--- a/Command/Expire.hs
+++ b/Command/Expire.hs
@@ -92,7 +92,7 @@
 data Expire = Expire (M.Map (Maybe UUID) (Maybe POSIXTime))
 
 parseExpire :: [String] -> Annex Expire
-parseExpire [] = error "Specify an expire time."
+parseExpire [] = giveup "Specify an expire time."
 parseExpire ps = do
 	now <- liftIO getPOSIXTime
 	Expire . M.fromList <$> mapM (parse now) ps
@@ -104,7 +104,7 @@
 			return (Just r, parsetime now t)
 	parsetime _ "never" = Nothing
 	parsetime now s = case parseDuration s of
-		Nothing -> error $ "bad expire time: " ++ s
+		Nothing -> giveup $ "bad expire time: " ++ s
 		Just d -> Just (now - durationToPOSIXTime d)
 
 parseActivity :: Monad m => String -> m Activity
diff --git a/Command/FromKey.hs b/Command/FromKey.hs
--- a/Command/FromKey.hs
+++ b/Command/FromKey.hs
@@ -33,14 +33,14 @@
 	let key = mkKey keyname
 	unless force $ do
 		inbackend <- inAnnex key
-		unless inbackend $ error $
+		unless inbackend $ giveup $
 			"key ("++ keyname ++") is not present in backend (use --force to override this sanity check)"
 	showStart "fromkey" file
 	next $ perform key file
 start _ [] = do
 	showStart "fromkey" "stdin"
 	next massAdd
-start _ _ = error "specify a key and a dest file"
+start _ _ = giveup "specify a key and a dest file"
 
 massAdd :: CommandPerform
 massAdd = go True =<< map (separate (== ' ')) . lines <$> liftIO getContents
@@ -51,7 +51,7 @@
 		ok <- perform' key f
 		let !status' = status && ok
 		go status' rest
-	go _ _ = error "Expected pairs of key and file on stdin, but got something else."
+	go _ _ = giveup "Expected pairs of key and file on stdin, but got something else."
 
 -- From user input to a Key.
 -- User can input either a serialized key, or an url.
@@ -66,7 +66,7 @@
 		Backend.URL.fromUrl s Nothing
 	_ -> case file2key s of
 		Just k -> k
-		Nothing -> error $ "bad key/url " ++ s
+		Nothing -> giveup $ "bad key/url " ++ s
 
 perform :: Key -> FilePath -> CommandPerform
 perform key file = do
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -89,7 +89,7 @@
 	checkDeadRepo u
 	i <- prepIncremental u (incrementalOpt o)
 	withKeyOptions (keyOptions o) False
-		(\k ai -> startKey i k ai =<< getNumCopies)
+		(\k ai -> startKey from i k ai =<< getNumCopies)
 		(withFilesInGit $ whenAnnexed $ start from i)
 		(fsckFiles o)
 	cleanupIncremental i
@@ -109,7 +109,7 @@
 			numcopies <- getFileNumCopies file
 			case from of
 				Nothing -> go $ perform key file backend numcopies
-				Just r -> go $ performRemote key file backend numcopies r
+				Just r -> go $ performRemote key (Just file) backend numcopies r
   where
 	go = runFsck inc (mkActionItem (Just file)) key
 
@@ -129,8 +129,8 @@
 
 {- To fsck a remote, the content is retrieved to a tmp file,
  - and checked locally. -}
-performRemote :: Key -> FilePath -> Backend -> NumCopies -> Remote -> Annex Bool
-performRemote key file backend numcopies remote =
+performRemote :: Key -> AssociatedFile -> Backend -> NumCopies -> Remote -> Annex Bool
+performRemote key afile backend numcopies remote =
 	dispatch =<< Remote.hasKey remote key
   where
 	dispatch (Left err) = do
@@ -147,10 +147,10 @@
 				return False
 	dispatch (Right False) = go False Nothing
 	go present localcopy = check
-		[ verifyLocationLogRemote key file remote present
+		[ verifyLocationLogRemote key (maybe (key2file key) id afile) remote present
 		, checkKeySizeRemote key remote localcopy
 		, checkBackendRemote backend key remote localcopy
-		, checkKeyNumCopies key (Just file) numcopies
+		, checkKeyNumCopies key afile numcopies
 		]
 	withtmp a = do
 		pid <- liftIO getPID
@@ -161,7 +161,7 @@
 		cleanup
 		cleanup `after` a tmp
 	getfile tmp = ifM (checkDiskSpace (Just (takeDirectory tmp)) key 0 True)
-		( ifM (Remote.retrieveKeyFileCheap remote key (Just file) tmp)
+		( ifM (Remote.retrieveKeyFileCheap remote key afile tmp)
 			( return (Just True)
 			, ifM (Annex.getState Annex.fast)
 				( return Nothing
@@ -173,12 +173,14 @@
 		)
 	dummymeter _ = noop
 
-startKey :: Incremental -> Key -> ActionItem -> NumCopies -> CommandStart
-startKey inc key ai numcopies =
+startKey :: Maybe Remote -> Incremental -> Key -> ActionItem -> NumCopies -> CommandStart
+startKey from inc key ai numcopies =
 	case Backend.maybeLookupBackendName (keyBackendName key) of
 		Nothing -> stop
 		Just backend -> runFsck inc ai key $
-			performKey key backend numcopies
+			case from of
+				Nothing -> performKey key backend numcopies
+				Just r -> performRemote key Nothing backend numcopies r
 
 performKey :: Key -> Backend -> NumCopies -> Annex Bool
 performKey key backend numcopies = do
@@ -584,7 +586,7 @@
 	recordStartTime u
 	ifM (FsckDb.newPass u)
 		( StartIncremental <$> openFsckDb u
-		, error "Cannot start a new --incremental fsck pass; another fsck process is already running."
+		, giveup "Cannot start a new --incremental fsck pass; another fsck process is already running."
 		)
 prepIncremental u (Just MoreIncrementalO) =
 	ContIncremental <$> openFsckDb u
diff --git a/Command/FuzzTest.hs b/Command/FuzzTest.hs
--- a/Command/FuzzTest.hs
+++ b/Command/FuzzTest.hs
@@ -39,7 +39,7 @@
 
 guardTest :: Annex ()
 guardTest = unlessM (fromMaybe False . Git.Config.isTrue <$> getConfig key "") $
-	error $ unlines
+	giveup $ unlines
 		[ "Running fuzz tests *writes* to and *deletes* files in"
 		, "this repository, and pushes those changes to other"
 		, "repositories! This is a developer tool, not something"
diff --git a/Command/GCryptSetup.hs b/Command/GCryptSetup.hs
--- a/Command/GCryptSetup.hs
+++ b/Command/GCryptSetup.hs
@@ -25,7 +25,7 @@
 start gcryptid = next $ next $ do
 	u <- getUUID
 	when (u /= NoUUID) $
-		error "gcryptsetup refusing to run; this repository already has a git-annex uuid!"
+		giveup "gcryptsetup refusing to run; this repository already has a git-annex uuid!"
 	
 	g <- gitRepo
 	gu <- Remote.GCrypt.getGCryptUUID True g
@@ -35,5 +35,5 @@
 			then do
 				void $ Remote.GCrypt.setupRepo gcryptid g
 				return True
-			else error "cannot use gcrypt in a non-bare repository"
-		else error "gcryptsetup uuid mismatch"
+			else giveup "cannot use gcrypt in a non-bare repository"
+		else giveup "gcryptsetup uuid mismatch"
diff --git a/Command/Group.hs b/Command/Group.hs
--- a/Command/Group.hs
+++ b/Command/Group.hs
@@ -30,7 +30,7 @@
 	u <- Remote.nameToUUID name
 	showRaw . unwords . S.toList =<< lookupGroups u
 	stop
-start _ = error "Specify a repository and a group."
+start _ = giveup "Specify a repository and a group."
 
 setGroup :: UUID -> Group -> CommandPerform
 setGroup uuid g = do
diff --git a/Command/GroupWanted.hs b/Command/GroupWanted.hs
--- a/Command/GroupWanted.hs
+++ b/Command/GroupWanted.hs
@@ -25,4 +25,4 @@
 start (g:expr:[]) = do
 	showStart "groupwanted" g
 	next $ performSet groupPreferredContentSet expr g
-start _ = error "Specify a group."
+start _ = giveup "Specify a group."
diff --git a/Command/Import.hs b/Command/Import.hs
--- a/Command/Import.hs
+++ b/Command/Import.hs
@@ -62,7 +62,7 @@
 	repopath <- liftIO . absPath =<< fromRepo Git.repoPath
 	inrepops <- liftIO $ filter (dirContains repopath) <$> mapM absPath (importFiles o)
 	unless (null inrepops) $ do
-		error $ "cannot import files from inside the working tree (use git annex add instead): " ++ unwords inrepops
+		giveup $ "cannot import files from inside the working tree (use git annex add instead): " ++ unwords inrepops
 	largematcher <- largeFilesMatcher
 	withPathContents (start largematcher (duplicateMode o)) (importFiles o)
 
diff --git a/Command/ImportFeed.hs b/Command/ImportFeed.hs
--- a/Command/ImportFeed.hs
+++ b/Command/ImportFeed.hs
@@ -147,7 +147,7 @@
 {- Feeds change, so a feed download cannot be resumed. -}
 downloadFeed :: URLString -> Annex (Maybe Feed)
 downloadFeed url
-	| Url.parseURIRelaxed url == Nothing = error "invalid feed url"
+	| Url.parseURIRelaxed url == Nothing = giveup "invalid feed url"
 	| otherwise = do
 		showOutput
 		uo <- Url.getUrlOptions
@@ -336,7 +336,7 @@
  - Throws an error if the feed is broken, otherwise shows a warning. -}
 feedProblem :: URLString -> String -> Annex ()
 feedProblem url message = ifM (checkFeedBroken url)
-	( error $ message ++ " (having repeated problems with feed: " ++ url ++ ")"
+	( giveup $ message ++ " (having repeated problems with feed: " ++ url ++ ")"
 	, warning $ "warning: " ++ message
 	)
 
diff --git a/Command/Indirect.hs b/Command/Indirect.hs
--- a/Command/Indirect.hs
+++ b/Command/Indirect.hs
@@ -33,9 +33,9 @@
 start = ifM isDirect
 	( do
 		unlessM (coreSymlinks <$> Annex.getGitConfig) $
-			error "Git is configured to not use symlinks, so you must use direct mode."
+			giveup "Git is configured to not use symlinks, so you must use direct mode."
 		whenM probeCrippledFileSystem $
-			error "This repository seems to be on a crippled filesystem, you must use direct mode."
+			giveup "This repository seems to be on a crippled filesystem, you must use direct mode."
 		next perform
 	, stop
 	)
diff --git a/Command/InitRemote.hs b/Command/InitRemote.hs
--- a/Command/InitRemote.hs
+++ b/Command/InitRemote.hs
@@ -26,16 +26,16 @@
 seek = withWords start
 
 start :: [String] -> CommandStart
-start [] = error "Specify a name for the remote."
+start [] = giveup "Specify a name for the remote."
 start (name:ws) = ifM (isJust <$> findExisting name)
-	( error $ "There is already a special remote named \"" ++ name ++
+	( giveup $ "There is already a special remote named \"" ++ name ++
 		"\". (Use enableremote to enable an existing special remote.)"
 	, do
 		ifM (isJust <$> Remote.byNameOnly name)
-			( error $ "There is already a remote named \"" ++ name ++ "\""
+			( giveup $ "There is already a remote named \"" ++ name ++ "\""
 			, do
 				let c = newConfig name
-				t <- either error return (findType config)
+				t <- either giveup return (findType config)
 
 				showStart "initremote" name
 				next $ perform t name $ M.union config c
diff --git a/Command/Lock.hs b/Command/Lock.hs
--- a/Command/Lock.hs
+++ b/Command/Lock.hs
@@ -79,7 +79,7 @@
 		unlessM (sameInodeCache obj (maybeToList mfc)) $ do
 			modifyContent obj $ replaceFile obj $ \tmp -> do
 				unlessM (checkedCopyFile key obj tmp Nothing) $
-					error "unable to lock file"
+					giveup "unable to lock file"
 			Database.Keys.storeInodeCaches key [obj]
 
 	-- Try to repopulate obj from an unmodified associated file.
@@ -115,4 +115,4 @@
 	next $ return True
 
 errorModified :: a
-errorModified =  error "Locking this file would discard any changes you have made to it. Use 'git annex add' to stage your changes. (Or, use --force to override)"
+errorModified =  giveup "Locking this file would discard any changes you have made to it. Use 'git annex add' to stage your changes. (Or, use --force to override)"
diff --git a/Command/LockContent.hs b/Command/LockContent.hs
--- a/Command/LockContent.hs
+++ b/Command/LockContent.hs
@@ -32,7 +32,7 @@
 		then exitSuccess
 		else exitFailure
   where
-	k = fromMaybe (error "bad key") (file2key ks)
+	k = fromMaybe (giveup "bad key") (file2key ks)
 	locksuccess = ifM (inAnnex k)
 		( liftIO $ do
 			putStrLn contentLockedMarker
@@ -41,4 +41,4 @@
 			return True
 		, return False
 		)
-start _ = error "Specify exactly 1 key."
+start _ = giveup "Specify exactly 1 key."
diff --git a/Command/Log.hs b/Command/Log.hs
--- a/Command/Log.hs
+++ b/Command/Log.hs
@@ -93,7 +93,7 @@
 	case (logFiles o, allOption o) of
 		(fs, False) -> withFilesInGit (whenAnnexed $ start o outputter) fs
 		([], True) -> commandAction (startAll o outputter)
-		(_, True) -> error "Cannot specify both files and --all"
+		(_, True) -> giveup "Cannot specify both files and --all"
 
 start :: LogOptions -> (FilePath -> Outputter) -> FilePath -> Key -> CommandStart
 start o outputter file key = do
diff --git a/Command/MetaData.hs b/Command/MetaData.hs
--- a/Command/MetaData.hs
+++ b/Command/MetaData.hs
@@ -81,7 +81,7 @@
 		Batch -> withMessageState $ \s -> case outputType s of
 			JSONOutput _ -> batchInput parseJSONInput $
 				commandAction . startBatch now
-			_ -> error "--batch is currently only supported in --json mode"
+			_ -> giveup "--batch is currently only supported in --json mode"
 
 start :: POSIXTime -> MetaDataOptions -> FilePath -> Key -> CommandStart
 start now o file k = startKeys now o k (mkActionItem afile)
@@ -156,7 +156,7 @@
 		mk <- lookupFile f
 		case mk of
 			Just k -> go k (mkActionItem (Just f))
-			Nothing -> error $ "not an annexed file: " ++ f
+			Nothing -> giveup $ "not an annexed file: " ++ f
 	Right k -> go k (mkActionItem k)
   where
 	go k ai = do
diff --git a/Command/Move.hs b/Command/Move.hs
--- a/Command/Move.hs
+++ b/Command/Move.hs
@@ -197,4 +197,4 @@
 			]
 		ok <- Remote.removeKey src key
 		next $ Command.Drop.cleanupRemote key src ok
-	faileddropremote = error "Unable to drop from remote."
+	faileddropremote = giveup "Unable to drop from remote."
diff --git a/Command/NumCopies.hs b/Command/NumCopies.hs
--- a/Command/NumCopies.hs
+++ b/Command/NumCopies.hs
@@ -23,15 +23,15 @@
 start :: [String] -> CommandStart
 start [] = startGet
 start [s] = case readish s of
-	Nothing -> error $ "Bad number: " ++ s
+	Nothing -> giveup $ "Bad number: " ++ s
 	Just n
 		| n > 0 -> startSet n
 		| n == 0 -> ifM (Annex.getState Annex.force)
 			( startSet n
-			, error "Setting numcopies to 0 is very unsafe. You will lose data! If you really want to do that, specify --force."
+			, giveup "Setting numcopies to 0 is very unsafe. You will lose data! If you really want to do that, specify --force."
 			)
-		| otherwise -> error "Number cannot be negative!"
-start _ = error "Specify a single number."
+		| otherwise -> giveup "Number cannot be negative!"
+start _ = giveup "Specify a single number."
 
 startGet :: CommandStart
 startGet = next $ next $ do
diff --git a/Command/PreCommit.hs b/Command/PreCommit.hs
--- a/Command/PreCommit.hs
+++ b/Command/PreCommit.hs
@@ -46,7 +46,7 @@
 			( do
 				(fs, cleanup) <- inRepo $ Git.typeChangedStaged ps
 				whenM (anyM isOldUnlocked fs) $
-					error "Cannot make a partial commit with unlocked annexed files. You should `git annex add` the files you want to commit, and then run git commit."
+					giveup "Cannot make a partial commit with unlocked annexed files. You should `git annex add` the files you want to commit, and then run git commit."
 				void $ liftIO cleanup
 			, do
 				-- fix symlinks to files being committed
diff --git a/Command/Proxy.hs b/Command/Proxy.hs
--- a/Command/Proxy.hs
+++ b/Command/Proxy.hs
@@ -30,7 +30,7 @@
 seek = withWords start
 
 start :: [String] -> CommandStart
-start [] = error "Did not specify command to run."
+start [] = giveup "Did not specify command to run."
 start (c:ps) = liftIO . exitWith =<< ifM isDirect
 	( do
 		tmp <- gitAnnexTmpMiscDir <$> gitRepo
diff --git a/Command/ReKey.hs b/Command/ReKey.hs
--- a/Command/ReKey.hs
+++ b/Command/ReKey.hs
@@ -33,7 +33,7 @@
 start :: (FilePath, String) -> CommandStart
 start (file, keyname) = ifAnnexed file go stop
   where
-	newkey = fromMaybe (error "bad key") $ file2key keyname
+	newkey = fromMaybe (giveup "bad key") $ file2key keyname
 	go oldkey
 		| oldkey == newkey = stop
 		| otherwise = do
@@ -46,7 +46,7 @@
 		( unlessM (linkKey file oldkey newkey) $
 			error "failed"
 		, unlessM (Annex.getState Annex.force) $
-			error $ file ++ " is not available (use --force to override)"
+			giveup $ file ++ " is not available (use --force to override)"
 		)
 	next $ cleanup file oldkey newkey
 
diff --git a/Command/ReadPresentKey.hs b/Command/ReadPresentKey.hs
--- a/Command/ReadPresentKey.hs
+++ b/Command/ReadPresentKey.hs
@@ -27,5 +27,5 @@
 		then liftIO exitSuccess
 		else liftIO exitFailure
   where
-	k = fromMaybe (error "bad key") (file2key ks)
-start _ = error "Wrong number of parameters"
+	k = fromMaybe (giveup "bad key") (file2key ks)
+start _ = giveup "Wrong number of parameters"
diff --git a/Command/RegisterUrl.hs b/Command/RegisterUrl.hs
--- a/Command/RegisterUrl.hs
+++ b/Command/RegisterUrl.hs
@@ -32,7 +32,7 @@
 start [] = do
 	showStart "registerurl" "stdin"
 	next massAdd
-start _ = error "specify a key and an url"
+start _ = giveup "specify a key and an url"
 
 massAdd :: CommandPerform
 massAdd = go True =<< map (separate (== ' ')) . lines <$> liftIO getContents
@@ -43,7 +43,7 @@
 		ok <- perform' key u
 		let !status' = status && ok
 		go status' rest
-	go _ _ = error "Expected pairs of key and url on stdin, but got something else."
+	go _ _ = giveup "Expected pairs of key and url on stdin, but got something else."
 
 perform :: Key -> URLString -> CommandPerform
 perform key url = do
diff --git a/Command/Reinject.hs b/Command/Reinject.hs
--- a/Command/Reinject.hs
+++ b/Command/Reinject.hs
@@ -47,7 +47,7 @@
 		next $ ifAnnexed dest
 			(\key -> perform src key (verifyKeyContent DefaultVerify UnVerified key src))
 			stop
-startSrcDest _ = error "specify a src file and a dest file"
+startSrcDest _ = giveup "specify a src file and a dest file"
 
 startKnown :: FilePath -> CommandStart
 startKnown src = notAnnexed src $ do
@@ -63,7 +63,8 @@
 			)
 
 notAnnexed :: FilePath -> CommandStart -> CommandStart
-notAnnexed src = ifAnnexed src (error $ "cannot used annexed file as src: " ++ src)
+notAnnexed src = ifAnnexed src $
+	giveup $ "cannot used annexed file as src: " ++ src
 
 perform :: FilePath -> Key -> Annex Bool -> CommandPerform
 perform src key verify = ifM move
diff --git a/Command/ResolveMerge.hs b/Command/ResolveMerge.hs
--- a/Command/ResolveMerge.hs
+++ b/Command/ResolveMerge.hs
@@ -33,8 +33,8 @@
 		( do
 			void $ commitResolvedMerge Git.Branch.ManualCommit
 			next $ next $ return True
-		, error "Merge conflict could not be automatically resolved."
+		, giveup "Merge conflict could not be automatically resolved."
 		)
   where
-	nobranch = error "No branch is currently checked out."
-	nomergehead = error "No SHA found in .git/merge_head"
+	nobranch = giveup "No branch is currently checked out."
+	nomergehead = giveup "No SHA found in .git/merge_head"
diff --git a/Command/Schedule.hs b/Command/Schedule.hs
--- a/Command/Schedule.hs
+++ b/Command/Schedule.hs
@@ -31,7 +31,7 @@
 	parse (name:expr:[]) = go name $ \uuid -> do
 		showStart "schedile" name
 		performSet expr uuid
-	parse _ = error "Specify a repository."
+	parse _ = giveup "Specify a repository."
 
 	go name a = do
 		u <- Remote.nameToUUID name
@@ -47,7 +47,7 @@
 
 performSet :: String -> UUID -> CommandPerform
 performSet expr uuid = case parseScheduledActivities expr of
-	Left e -> error $ "Parse error: " ++ e
+	Left e -> giveup $ "Parse error: " ++ e
 	Right l -> do
 		scheduleSet uuid l
 		next $ return True
diff --git a/Command/SetKey.hs b/Command/SetKey.hs
--- a/Command/SetKey.hs
+++ b/Command/SetKey.hs
@@ -23,10 +23,10 @@
 start (keyname:file:[]) = do
 	showStart "setkey" file
 	next $ perform file (mkKey keyname)
-start _ = error "specify a key and a content file"
+start _ = giveup "specify a key and a content file"
 
 mkKey :: String -> Key
-mkKey = fromMaybe (error "bad key") . file2key
+mkKey = fromMaybe (giveup "bad key") . file2key
 
 perform :: FilePath -> Key -> CommandPerform
 perform file key = do
diff --git a/Command/SetPresentKey.hs b/Command/SetPresentKey.hs
--- a/Command/SetPresentKey.hs
+++ b/Command/SetPresentKey.hs
@@ -26,9 +26,9 @@
 	showStart' "setpresentkey" k (mkActionItem k)
 	next $ perform k (toUUID us) s
   where
-	k = fromMaybe (error "bad key") (file2key ks)
-	s = fromMaybe (error "bad value") (parseStatus vs)
-start _ = error "Wrong number of parameters"
+	k = fromMaybe (giveup "bad key") (file2key ks)
+	s = fromMaybe (giveup "bad value") (parseStatus vs)
+start _ = giveup "Wrong number of parameters"
 
 perform :: Key -> UUID -> LogStatus -> CommandPerform
 perform k u s = next $ do
diff --git a/Command/Sync.hs b/Command/Sync.hs
--- a/Command/Sync.hs
+++ b/Command/Sync.hs
@@ -169,7 +169,15 @@
 prepMerge = Annex.changeDirectory =<< fromRepo Git.repoPath
 
 mergeConfig :: [Git.Merge.MergeConfig]	
-mergeConfig = [Git.Merge.MergeNonInteractive]
+mergeConfig = 
+	[ Git.Merge.MergeNonInteractive
+	-- In several situations, unrelated histories should be merged
+	-- together. This includes pairing in the assistant, and merging
+	-- from a remote into a newly created direct mode repo.
+	-- (Once direct mode is removed, this could be changed, so only
+	-- the assistant uses it.)
+	, Git.Merge.MergeUnrelatedHistories
+	]
 
 merge :: CurrBranch -> [Git.Merge.MergeConfig] -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool
 merge (Just b, Just adj) mergeconfig commitmode tomerge =
@@ -287,7 +295,7 @@
 
 updateBranch :: Git.Branch -> Git.Branch -> Git.Repo -> IO ()
 updateBranch syncbranch updateto g = 
-	unlessM go $ error $ "failed to update " ++ Git.fromRef syncbranch
+	unlessM go $ giveup $ "failed to update " ++ Git.fromRef syncbranch
   where
 	go = Git.Command.runBool
 		[ Param "branch"
diff --git a/Command/TestRemote.hs b/Command/TestRemote.hs
--- a/Command/TestRemote.hs
+++ b/Command/TestRemote.hs
@@ -57,7 +57,7 @@
 start :: Int -> RemoteName -> CommandStart
 start basesz name = do
 	showStart "testremote" name
-	r <- either error id <$> Remote.byName' name
+	r <- either giveup id <$> Remote.byName' name
 	showAction "generating test keys"
 	fast <- Annex.getState Annex.fast
 	ks <- mapM randKey (keySizes basesz fast)
diff --git a/Command/TransferInfo.hs b/Command/TransferInfo.hs
--- a/Command/TransferInfo.hs
+++ b/Command/TransferInfo.hs
@@ -59,7 +59,7 @@
 				, exitSuccess
 				]
 	stop
-start _ = error "wrong number of parameters"
+start _ = giveup "wrong number of parameters"
 
 readUpdate :: IO (Maybe Integer)
 readUpdate = readish <$> getLine
diff --git a/Command/Unannex.hs b/Command/Unannex.hs
--- a/Command/Unannex.hs
+++ b/Command/Unannex.hs
@@ -45,7 +45,7 @@
 	 -}
 	, ifM cleanindex
 		( lockPreCommitHook $ commit `after` a
-		, error "Cannot proceed with uncommitted changes staged in the index. Recommend you: git commit"
+		, giveup "Cannot proceed with uncommitted changes staged in the index. Recommend you: git commit"
 		)
 	)
   where
diff --git a/Command/Undo.hs b/Command/Undo.hs
--- a/Command/Undo.hs
+++ b/Command/Undo.hs
@@ -32,7 +32,7 @@
 	-- in the index.
 	(fs, cleanup) <- inRepo $ LsFiles.notInRepo False ps
 	unless (null fs) $
-		error $ "Cannot undo changes to files that are not checked into git: " ++ unwords fs
+		giveup $ "Cannot undo changes to files that are not checked into git: " ++ unwords fs
 	void $ liftIO $ cleanup
 
 	-- Committing staged changes before undo allows later
diff --git a/Command/Ungroup.hs b/Command/Ungroup.hs
--- a/Command/Ungroup.hs
+++ b/Command/Ungroup.hs
@@ -26,7 +26,7 @@
 	showStart "ungroup" name
 	u <- Remote.nameToUUID name
 	next $ perform u g
-start _ = error "Specify a repository and a group."
+start _ = giveup "Specify a repository and a group."
 
 perform :: UUID -> Group -> CommandPerform
 perform uuid g = do
diff --git a/Command/Uninit.hs b/Command/Uninit.hs
--- a/Command/Uninit.hs
+++ b/Command/Uninit.hs
@@ -30,12 +30,12 @@
 check :: Annex ()
 check = do
 	b <- current_branch
-	when (b == Annex.Branch.name) $ error $
+	when (b == Annex.Branch.name) $ giveup $
 		"cannot uninit when the " ++ Git.fromRef b ++ " branch is checked out"
 	top <- fromRepo Git.repoPath
 	currdir <- liftIO getCurrentDirectory
 	whenM ((/=) <$> liftIO (absPath top) <*> liftIO (absPath currdir)) $
-		error "can only run uninit from the top of the git repository"
+		giveup "can only run uninit from the top of the git repository"
   where
 	current_branch = Git.Ref . Prelude.head . lines <$> revhead
 	revhead = inRepo $ Git.Command.pipeReadStrict
@@ -51,7 +51,7 @@
 {- git annex symlinks that are not checked into git could be left by an
  - interrupted add. -}
 startCheckIncomplete :: FilePath -> Key -> CommandStart
-startCheckIncomplete file _ = error $ unlines
+startCheckIncomplete file _ = giveup $ unlines
 	[ file ++ " points to annexed content, but is not checked into git."
 	, "Perhaps this was left behind by an interrupted git annex add?"
 	, "Not continuing with uninit; either delete or git annex add the file and retry."
@@ -65,7 +65,7 @@
 	prepareRemoveAnnexDir annexdir
 	if null leftovers
 		then liftIO $ removeDirectoryRecursive annexdir
-		else error $ unlines
+		else giveup $ unlines
 			[ "Not fully uninitialized"
 			, "Some annexed data is still left in " ++ annexobjectdir
 			, "This may include deleted files, or old versions of modified files."
diff --git a/Command/Unused.hs b/Command/Unused.hs
--- a/Command/Unused.hs
+++ b/Command/Unused.hs
@@ -320,7 +320,7 @@
 	range (a, b) = case (readish a, readish b) of
 		(Just x, Just y) -> [x..y]
 		_ -> badspec
-	badspec = error $ "Expected number or range, not \"" ++ spec ++ "\""
+	badspec = giveup $ "Expected number or range, not \"" ++ spec ++ "\""
 
 {- Seek action for unused content. Finds the number in the maps, and
  - calls one of 3 actions, depending on the type of unused file. -}
@@ -335,7 +335,7 @@
 	, (unusedTmpMap maps, tmpunused)
 	]
   where
-	search [] = error $ show n ++ " not valid (run git annex unused for list)"
+	search [] = giveup $ show n ++ " not valid (run git annex unused for list)"
 	search ((m, a):rest) =
 		case M.lookup n m of
 			Nothing -> search rest
diff --git a/Command/VAdd.hs b/Command/VAdd.hs
--- a/Command/VAdd.hs
+++ b/Command/VAdd.hs
@@ -33,6 +33,6 @@
 				next $ next $ return True
 			Narrowing -> next $ next $ do
 				if visibleViewSize view' == visibleViewSize view
-					then error "That would not add an additional level of directory structure to the view. To filter the view, use vfilter instead of vadd."
+					then giveup "That would not add an additional level of directory structure to the view. To filter the view, use vfilter instead of vadd."
 					else checkoutViewBranch view' narrowView
-			Widening -> error "Widening view to match more files is not currently supported."
+			Widening -> giveup "Widening view to match more files is not currently supported."
diff --git a/Command/VCycle.hs b/Command/VCycle.hs
--- a/Command/VCycle.hs
+++ b/Command/VCycle.hs
@@ -25,7 +25,7 @@
 start ::CommandStart
 start = go =<< currentView
   where
-	go Nothing = error "Not in a view."
+	go Nothing = giveup "Not in a view."
 	go (Just v) = do
 		showStart "vcycle" ""
 		let v' = v { viewComponents = vcycle [] (viewComponents v) }
diff --git a/Command/VFilter.hs b/Command/VFilter.hs
--- a/Command/VFilter.hs
+++ b/Command/VFilter.hs
@@ -26,5 +26,5 @@
 		let view' = filterView view $
 			map parseViewParam $ reverse params
 		next $ next $ if visibleViewSize view' > visibleViewSize view
-			then error "That would add an additional level of directory structure to the view, rather than filtering it. If you want to do that, use vadd instead of vfilter."
+			then giveup "That would add an additional level of directory structure to the view, rather than filtering it. If you want to do that, use vadd instead of vfilter."
 			else checkoutViewBranch view' narrowView
diff --git a/Command/VPop.hs b/Command/VPop.hs
--- a/Command/VPop.hs
+++ b/Command/VPop.hs
@@ -26,7 +26,7 @@
 start :: [String] -> CommandStart
 start ps = go =<< currentView
   where
-	go Nothing = error "Not in a view."
+	go Nothing = giveup "Not in a view."
 	go (Just v) = do
 		showStart "vpop" (show num)
 		removeView v
diff --git a/Command/Vicfg.hs b/Command/Vicfg.hs
--- a/Command/Vicfg.hs
+++ b/Command/Vicfg.hs
@@ -50,7 +50,7 @@
 	vi <- liftIO $ catchDefaultIO "vi" $ getEnv "EDITOR"
 	-- Allow EDITOR to be processed by the shell, so it can contain options.
 	unlessM (liftIO $ boolSystem "sh" [Param "-c", Param $ unwords [vi, shellEscape f]]) $
-		error $ vi ++ " exited nonzero; aborting"
+		giveup $ vi ++ " exited nonzero; aborting"
 	r <- parseCfg (defCfg curcfg) <$> liftIO (readFileStrictAnyEncoding f)
 	liftIO $ nukeFile f
 	case r of
diff --git a/Command/View.hs b/Command/View.hs
--- a/Command/View.hs
+++ b/Command/View.hs
@@ -25,7 +25,7 @@
 seek = withWords start
 
 start :: [String] -> CommandStart
-start [] = error "Specify metadata to include in view"
+start [] = giveup "Specify metadata to include in view"
 start ps = do
 	showStart "view" ""
 	view <- mkView ps
@@ -34,7 +34,7 @@
 	go view Nothing = next $ perform view
 	go view (Just v)
 		| v == view = stop
-		| otherwise = error "Already in a view. Use the vfilter and vadd commands to further refine this view."
+		| otherwise = giveup "Already in a view. Use the vfilter and vadd commands to further refine this view."
 
 perform :: View -> CommandPerform
 perform view = do
@@ -47,7 +47,7 @@
 mkView :: [String] -> Annex View
 mkView ps = go =<< inRepo Git.Branch.current
   where
-	go Nothing = error "not on any branch!"
+	go Nothing = giveup "not on any branch!"
 	go (Just b) = return $ fst $ refineView (View b []) $
 		map parseViewParam $ reverse ps
 
diff --git a/Command/Wanted.hs b/Command/Wanted.hs
--- a/Command/Wanted.hs
+++ b/Command/Wanted.hs
@@ -37,7 +37,7 @@
 	start (rname:expr:[]) = go rname $ \uuid -> do
 		showStart name rname
 		performSet setter expr uuid
-	start _ = error "Specify a repository."
+	start _ = giveup "Specify a repository."
 		
 	go rname a = do
 		u <- Remote.nameToUUID rname
@@ -52,7 +52,7 @@
 
 performSet :: (a -> PreferredContentExpression -> Annex ()) -> String -> a -> CommandPerform
 performSet setter expr a = case checkPreferredContentExpression expr of
-	Just e -> error $ "Parse error: " ++ e
+	Just e -> giveup $ "Parse error: " ++ e
 	Nothing -> do
 		setter a expr
 		next $ return True
diff --git a/Command/WebApp.hs b/Command/WebApp.hs
--- a/Command/WebApp.hs
+++ b/Command/WebApp.hs
@@ -77,7 +77,7 @@
 			else annexListen <$> Annex.getGitConfig
 		ifM (checkpid <&&> checkshim f)
 			( if isJust (listenAddress o)
-				then error "The assistant is already running, so --listen cannot be used."
+				then giveup "The assistant is already running, so --listen cannot be used."
 				else do
 					url <- liftIO . readFile
 						=<< fromRepo gitAnnexUrlFile
@@ -125,7 +125,7 @@
 				go ds
 			Right state -> void $ Annex.eval state $ do
 				whenM (fromRepo Git.repoIsLocalBare) $
-					error $ d ++ " is a bare git repository, cannot run the webapp in it"
+					giveup $ d ++ " is a bare git repository, cannot run the webapp in it"
 				callCommandAction $
 					start' False o
 
diff --git a/Config/Files.hs b/Config/Files.hs
--- a/Config/Files.hs
+++ b/Config/Files.hs
@@ -80,4 +80,4 @@
 cannotFindProgram :: IO a
 cannotFindProgram = do
 	f <- programFile
-	error $ "cannot find git-annex program in PATH or in the location listed in " ++ f
+	giveup $ "cannot find git-annex program in PATH or in the location listed in " ++ f
diff --git a/Creds.hs b/Creds.hs
--- a/Creds.hs
+++ b/Creds.hs
@@ -105,7 +105,7 @@
 				-- Not a problem for shared cipher.
 				case storablecipher of
 					SharedCipher {} -> showLongNote "gpg error above was caused by an old git-annex bug in credentials storage. Working around it.."
-					_ -> error "*** Insecure credentials storage detected for this remote! See https://git-annex.branchable.com/upgrades/insecure_embedded_creds/"
+					_ -> giveup "*** Insecure credentials storage detected for this remote! See https://git-annex.branchable.com/upgrades/insecure_embedded_creds/"
 				fromcreds $ fromB64 enccreds
 	fromcreds creds = case decodeCredPair creds of
 		Just credpair -> do
diff --git a/Crypto.hs b/Crypto.hs
--- a/Crypto.hs
+++ b/Crypto.hs
@@ -100,7 +100,7 @@
  -
  - When the Cipher is encrypted, re-encrypts it. -}
 updateCipherKeyIds :: LensGpgEncParams encparams => Gpg.GpgCmd -> encparams -> [(Bool, Gpg.KeyId)] -> StorableCipher -> IO StorableCipher
-updateCipherKeyIds _ _ _ SharedCipher{} = error "Cannot update shared cipher"
+updateCipherKeyIds _ _ _ SharedCipher{} = giveup "Cannot update shared cipher"
 updateCipherKeyIds _ _ [] c = return c
 updateCipherKeyIds cmd encparams changes encipher@(EncryptedCipher _ variant ks) = do
 	ks' <- updateCipherKeyIds' cmd changes ks
@@ -113,11 +113,11 @@
 updateCipherKeyIds' cmd changes (KeyIds ks) = do
 	dropkeys <- listKeyIds [ k | (False, k) <- changes ]
 	forM_ dropkeys $ \k -> unless (k `elem` ks) $
-		error $ "Key " ++ k ++ " was not present; cannot remove."
+		giveup $ "Key " ++ k ++ " was not present; cannot remove."
 	addkeys <- listKeyIds [ k | (True, k) <- changes ]
 	let ks' = (addkeys ++ ks) \\ dropkeys
 	when (null ks') $
-		error "Cannot remove the last key."
+		giveup "Cannot remove the last key."
 	return $ KeyIds ks'
   where
 	listKeyIds = concat <$$> mapM (keyIds <$$> Gpg.findPubKeys cmd)
diff --git a/Database/Types.hs b/Database/Types.hs
--- a/Database/Types.hs
+++ b/Database/Types.hs
@@ -25,7 +25,7 @@
 toSKey = SKey . key2file
 
 fromSKey :: SKey -> Key
-fromSKey (SKey s) = fromMaybe (error $ "bad serialied Key " ++ s) (file2key s)
+fromSKey (SKey s) = fromMaybe (error $ "bad serialized Key " ++ s) (file2key s)
 
 derivePersistField "SKey"
 
@@ -43,7 +43,7 @@
 toIKey = IKey . key2file
 
 fromIKey :: IKey -> Key
-fromIKey (IKey s) = fromMaybe (error $ "bad serialied Key " ++ s) (file2key s)
+fromIKey (IKey s) = fromMaybe (error $ "bad serialized Key " ++ s) (file2key s)
 
 derivePersistField "IKey"
 
diff --git a/Git/AutoCorrect.hs b/Git/AutoCorrect.hs
--- a/Git/AutoCorrect.hs
+++ b/Git/AutoCorrect.hs
@@ -50,7 +50,7 @@
 			| otherwise -> sleep n
 		Nothing -> list
   where
-	list = error $ unlines $
+	list = giveup $ unlines $
 		[ "Unknown command '" ++ input ++ "'"
 		, ""
 		, "Did you mean one of these?"
diff --git a/Git/CurrentRepo.hs b/Git/CurrentRepo.hs
--- a/Git/CurrentRepo.hs
+++ b/Git/CurrentRepo.hs
@@ -52,7 +52,7 @@
 		curr <- getCurrentDirectory
 		Git.Config.read $ newFrom $
 			Local { gitdir = absd, worktree = Just curr }
-	configure Nothing Nothing = error "Not in a git repository."
+	configure Nothing Nothing = giveup "Not in a git repository."
 
 	addworktree w r = changelocation r $
 		Local { gitdir = gitdir (location r), worktree = w }
diff --git a/Git/GCrypt.hs b/Git/GCrypt.hs
--- a/Git/GCrypt.hs
+++ b/Git/GCrypt.hs
@@ -46,7 +46,7 @@
 		u = show url
 		plen = length urlPrefix
 	go _ = notencrypted
-	notencrypted = error "not a gcrypt encrypted repository"
+	notencrypted = giveup "not a gcrypt encrypted repository"
 
 data ProbeResult = Decryptable | NotDecryptable | NotEncrypted
 
diff --git a/Limit.hs b/Limit.hs
--- a/Limit.hs
+++ b/Limit.hs
@@ -73,7 +73,7 @@
 
 {- Adds a new limit. -}
 addLimit :: Either String (MatchFiles Annex) -> Annex ()
-addLimit = either error (\l -> add $ Utility.Matcher.Operation $ l S.empty)
+addLimit = either giveup (\l -> add $ Utility.Matcher.Operation $ l S.empty)
 
 {- Add a limit to skip files that do not match the glob. -}
 addInclude :: String -> Annex ()
@@ -289,7 +289,7 @@
 
 addTimeLimit :: String -> Annex ()
 addTimeLimit s = do
-	let seconds = maybe (error "bad time-limit") durationToPOSIXTime $
+	let seconds = maybe (giveup "bad time-limit") durationToPOSIXTime $
 		parseDuration s
 	start <- liftIO getPOSIXTime
 	let cutoff = start + seconds
diff --git a/Logs/Transitions.hs b/Logs/Transitions.hs
--- a/Logs/Transitions.hs
+++ b/Logs/Transitions.hs
@@ -60,7 +60,7 @@
 parseTransitionsStrictly :: String -> String -> Transitions
 parseTransitionsStrictly source = fromMaybe badsource . parseTransitions
   where
-	badsource = error $ "unknown transitions listed in " ++ source ++ "; upgrade git-annex!"
+	badsource = giveup $ "unknown transitions listed in " ++ source ++ "; upgrade git-annex!"
 
 showTransitionLine :: TransitionLine -> String
 showTransitionLine (TransitionLine ts t) = unwords [show t, show ts]
diff --git a/Remote.hs b/Remote.hs
--- a/Remote.hs
+++ b/Remote.hs
@@ -112,7 +112,7 @@
  -}
 byName :: Maybe RemoteName -> Annex (Maybe Remote)
 byName Nothing = return Nothing
-byName (Just n) = either error Just <$> byName' n
+byName (Just n) = either giveup Just <$> byName' n
 
 {- Like byName, but the remote must have a configured UUID. -}
 byNameWithUUID :: Maybe RemoteName -> Annex (Maybe Remote)
@@ -120,7 +120,7 @@
   where
 	checkuuid Nothing = return Nothing
 	checkuuid (Just r)
-		| uuid r == NoUUID = error $
+		| uuid r == NoUUID = giveup $
 			if remoteAnnexIgnore (gitconfig r)
 				then noRemoteUUIDMsg r ++
 					" (" ++ show (remoteConfig (repo r) "ignore") ++
@@ -156,7 +156,7 @@
  - and returns its UUID. Finds even repositories that are not
  - configured in .git/config. -}
 nameToUUID :: RemoteName -> Annex UUID
-nameToUUID = either error return <=< nameToUUID'
+nameToUUID = either giveup return <=< nameToUUID'
 
 nameToUUID' :: RemoteName -> Annex (Either String UUID)
 nameToUUID' "." = Right <$> getUUID -- special case for current repo
diff --git a/Remote/BitTorrent.hs b/Remote/BitTorrent.hs
--- a/Remote/BitTorrent.hs
+++ b/Remote/BitTorrent.hs
@@ -111,7 +111,7 @@
  - implemented, it tells us nothing about the later state of the torrent.
  -}
 checkKey :: Key -> Annex Bool
-checkKey = error "cannot reliably check torrent status"
+checkKey = giveup "cannot reliably check torrent status"
 
 getBitTorrentUrls :: Key -> Annex [URLString]
 getBitTorrentUrls key = filter supported <$> getUrls key
@@ -138,7 +138,7 @@
 	registerTorrentCleanup u
 	ifM (downloadTorrentFile u)
 		( torrentContents u
-		, error "could not download torrent file"
+		, giveup "could not download torrent file"
 		)
 
 {- To specify which file inside a multi-url torrent, the file number is
@@ -268,13 +268,13 @@
 		fs <- liftIO $ map fst <$> torrentFileSizes torrent
 		if length fs >= filenum
 			then return (fs !! (filenum - 1))
-			else error "Number of files in torrent seems to have changed."
+			else giveup "Number of files in torrent seems to have changed."
 
 checkDependencies :: Annex ()
 checkDependencies = do
 	missing <- liftIO $ filterM (not <$$> inPath) deps
 	unless (null missing) $
-		error $ "need to install additional software in order to download from bittorrent: " ++ unwords missing
+		giveup $ "need to install additional software in order to download from bittorrent: " ++ unwords missing
   where
 	deps =
 		[ "aria2c"
@@ -343,7 +343,7 @@
 	let mkfile = joinPath . map (scrub . decodeBS)
 	b <- B.readFile torrent
 	return $ case readTorrent b of
-		Left e -> error $ "failed to parse torrent: " ++ e
+		Left e -> giveup $ "failed to parse torrent: " ++ e
 		Right t -> case tInfo t of
 			SingleFile { tLength = l, tName = f } ->
 				[ (mkfile [f], l) ]
@@ -366,7 +366,7 @@
 				_ -> parsefailed (show v)
   where
 	getfield = btshowmetainfo torrent
-	parsefailed s = error $ "failed to parse btshowmetainfo output for torrent file: " ++ show s
+	parsefailed s = giveup $ "failed to parse btshowmetainfo output for torrent file: " ++ show s
 
 	-- btshowmetainfo outputs a list of "filename (size)"
 	splitsize d l = (scrub (d </> fn), sz)
@@ -379,7 +379,7 @@
 #endif
 	-- a malicious torrent file might try to do directory traversal
 	scrub f = if isAbsolute f || any (== "..") (splitPath f)
-		then error "found unsafe filename in torrent!"
+		then giveup "found unsafe filename in torrent!"
 		else f
 
 torrentContents :: URLString -> Annex UrlContents
diff --git a/Remote/Bup.hs b/Remote/Bup.hs
--- a/Remote/Bup.hs
+++ b/Remote/Bup.hs
@@ -84,7 +84,7 @@
 		(simplyPrepare $ checkKey r bupr')
 		this
   where
-	buprepo = fromMaybe (error "missing buprepo") $ remoteAnnexBupRepo gc
+	buprepo = fromMaybe (giveup "missing buprepo") $ remoteAnnexBupRepo gc
 	specialcfg = (specialRemoteCfg c)
 		-- chunking would not improve bup
 		{ chunkConfig = NoChunks
@@ -95,14 +95,14 @@
 	u <- maybe (liftIO genUUID) return mu
 
 	-- verify configuration is sane
-	let buprepo = fromMaybe (error "Specify buprepo=") $
+	let buprepo = fromMaybe (giveup "Specify buprepo=") $
 		M.lookup "buprepo" c
 	(c', _encsetup) <- encryptionSetup c gc
 
 	-- bup init will create the repository.
 	-- (If the repository already exists, bup init again appears safe.)
 	showAction "bup init"
-	unlessM (bup "init" buprepo []) $ error "bup init failed"
+	unlessM (bup "init" buprepo []) $ giveup "bup init failed"
 
 	storeBupUUID u buprepo
 
@@ -197,7 +197,7 @@
 			showAction "storing uuid"
 			unlessM (onBupRemote r boolSystem "git"
 				[Param "config", Param "annex.uuid", Param v]) $
-					error "ssh failed"
+					giveup "ssh failed"
 		else liftIO $ do
 			r' <- Git.Config.read r
 			let olduuid = Git.Config.get "annex.uuid" "" r'
@@ -251,7 +251,7 @@
 	| bupLocal r = 
 		if "/" `isPrefixOf` r
 			then Git.Construct.fromAbsPath r
-			else error "please specify an absolute path"
+			else giveup "please specify an absolute path"
 	| otherwise = Git.Construct.fromUrl $ "ssh://" ++ host ++ slash dir
   where
 	bits = split ":" r
diff --git a/Remote/Ddar.hs b/Remote/Ddar.hs
--- a/Remote/Ddar.hs
+++ b/Remote/Ddar.hs
@@ -76,7 +76,7 @@
 		, claimUrl = Nothing
 		, checkUrl = Nothing
 		}
-	ddarrepo = maybe (error "missing ddarrepo") (DdarRepo gc) (remoteAnnexDdarRepo gc)
+	ddarrepo = maybe (giveup "missing ddarrepo") (DdarRepo gc) (remoteAnnexDdarRepo gc)
 	specialcfg = (specialRemoteCfg c)
 		-- chunking would not improve ddar
 		{ chunkConfig = NoChunks
@@ -87,7 +87,7 @@
 	u <- maybe (liftIO genUUID) return mu
 
 	-- verify configuration is sane
-	let ddarrepo = fromMaybe (error "Specify ddarrepo=") $
+	let ddarrepo = fromMaybe (giveup "Specify ddarrepo=") $
 		M.lookup "ddarrepo" c
 	(c', _encsetup) <- encryptionSetup c gc
 
diff --git a/Remote/Directory.hs b/Remote/Directory.hs
--- a/Remote/Directory.hs
+++ b/Remote/Directory.hs
@@ -75,17 +75,17 @@
 			, checkUrl = Nothing
 			}
   where
-	dir = fromMaybe (error "missing directory") $ remoteAnnexDirectory gc
+	dir = fromMaybe (giveup "missing directory") $ remoteAnnexDirectory gc
 
 directorySetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
 directorySetup mu _ c gc = do
 	u <- maybe (liftIO genUUID) return mu
 	-- verify configuration is sane
-	let dir = fromMaybe (error "Specify directory=") $
+	let dir = fromMaybe (giveup "Specify directory=") $
 		M.lookup "directory" c
 	absdir <- liftIO $ absPath dir
 	liftIO $ unlessM (doesDirectoryExist absdir) $
-		error $ "Directory does not exist: " ++ absdir
+		giveup $ "Directory does not exist: " ++ absdir
 	(c', _encsetup) <- encryptionSetup c gc
 
 	-- The directory is stored in git config, not in this remote's
@@ -216,6 +216,6 @@
 		( return True
 		, ifM (doesDirectoryExist d)
 			( return False
-			, error $ "directory " ++ d ++ " is not accessible"
+			, giveup $ "directory " ++ d ++ " is not accessible"
 			)
 		)
diff --git a/Remote/External.hs b/Remote/External.hs
--- a/Remote/External.hs
+++ b/Remote/External.hs
@@ -107,12 +107,12 @@
 			(simplyPrepare toremove)
 			(simplyPrepare tocheckkey)
 			rmt
-	externaltype = fromMaybe (error "missing externaltype") (remoteAnnexExternalType gc)
+	externaltype = fromMaybe (giveup "missing externaltype") (remoteAnnexExternalType gc)
 
 externalSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
 externalSetup mu _ c gc = do
 	u <- maybe (liftIO genUUID) return mu
-	let externaltype = fromMaybe (error "Specify externaltype=") $
+	let externaltype = fromMaybe (giveup "Specify externaltype=") $
 		M.lookup "externaltype" c
 	(c', _encsetup) <- encryptionSetup c gc
 
@@ -124,7 +124,7 @@
 			external <- newExternal externaltype u c' gc
 			handleRequest external INITREMOTE Nothing $ \resp -> case resp of
 				INITREMOTE_SUCCESS -> Just noop
-				INITREMOTE_FAILURE errmsg -> Just $ error errmsg
+				INITREMOTE_FAILURE errmsg -> Just $ giveup errmsg
 				_ -> Nothing
 			withExternalState external $
 				liftIO . atomically . readTVar . externalConfig
@@ -151,8 +151,7 @@
 			TRANSFER_SUCCESS Download k'
 				| k == k' -> Just $ return ()
 			TRANSFER_FAILURE Download k' errmsg
-				| k == k' -> Just $ do
-					error errmsg
+				| k == k' -> Just $ giveup errmsg
 			_ -> Nothing
 
 remove :: External -> Remover
@@ -168,7 +167,7 @@
 			_ -> Nothing
 
 checkKey :: External -> CheckPresent
-checkKey external k = either error id <$> go
+checkKey external k = either giveup id <$> go
   where
 	go = handleRequest external (CHECKPRESENT k) Nothing $ \resp ->
 		case resp of
@@ -284,7 +283,7 @@
 	handleRemoteRequest (VERSION _) =
 		sendMessage st external (ERROR "too late to send VERSION")
 
-	handleAsyncMessage (ERROR err) = error $ "external special remote error: " ++ err
+	handleAsyncMessage (ERROR err) = giveup $ "external special remote error: " ++ err
 
 	send = sendMessage st external
 
@@ -332,7 +331,7 @@
 				Nothing -> case parseMessage s :: Maybe AsyncMessage of
 					Just msg -> maybe (protocolError True s) id (handleasync msg)
 					Nothing -> protocolError False s
-	protocolError parsed s = error $ "external special remote protocol error, unexpectedly received \"" ++ s ++ "\" " ++
+	protocolError parsed s = giveup $ "external special remote protocol error, unexpectedly received \"" ++ s ++ "\" " ++
 		if parsed then "(command not allowed at this time)" else "(unable to parse command)"
 
 protocolDebug :: External -> ExternalState -> Bool -> String -> IO ()
@@ -413,14 +412,14 @@
 		environ <- propGitEnv g
 		return $ p { env = Just environ }
 
-	runerr _ = error ("Cannot run " ++ basecmd ++ " -- Make sure it's in your PATH and is executable.")
+	runerr _ = giveup ("Cannot run " ++ basecmd ++ " -- Make sure it's in your PATH and is executable.")
 
 	checkearlytermination Nothing = noop
 	checkearlytermination (Just exitcode) = ifM (inPath basecmd)
-		( error $ unwords [ "failed to run", basecmd, "(" ++ show exitcode ++ ")" ]
+		( giveup $ unwords [ "failed to run", basecmd, "(" ++ show exitcode ++ ")" ]
 		, do
 			path <- intercalate ":" <$> getSearchPath
-			error $ basecmd ++ " is not installed in PATH (" ++ path ++ ")"
+			giveup $ basecmd ++ " is not installed in PATH (" ++ path ++ ")"
 		)
 
 stopExternal :: External -> Annex ()
@@ -452,7 +451,7 @@
 	v <- liftIO $ atomically $ readTVar $ externalPrepared st
 	case v of
 		Prepared -> noop
-		FailedPrepare errmsg -> error errmsg
+		FailedPrepare errmsg -> giveup errmsg
 		Unprepared ->
 			handleRequest' st external PREPARE Nothing $ \resp ->
 				case resp of
@@ -460,7 +459,7 @@
 						setprepared Prepared
 					PREPARE_FAILURE errmsg -> Just $ do
 						setprepared $ FailedPrepare errmsg
-						error errmsg
+						giveup errmsg
 					_ -> Nothing
   where
 	setprepared status = liftIO $ atomically $ void $
@@ -520,8 +519,8 @@
 		CHECKURL_MULTI ((_, sz, f):[]) ->
 			Just $ return $ UrlContents sz $ Just $ mkSafeFilePath f
 		CHECKURL_MULTI l -> Just $ return $ UrlMulti $ map mkmulti l
-		CHECKURL_FAILURE errmsg -> Just $ error errmsg
-		UNSUPPORTED_REQUEST -> error "CHECKURL not implemented by external special remote"
+		CHECKURL_FAILURE errmsg -> Just $ giveup errmsg
+		UNSUPPORTED_REQUEST -> giveup "CHECKURL not implemented by external special remote"
 		_ -> Nothing
   where
 	mkmulti (u, s, f) = (u, s, mkSafeFilePath f)
@@ -530,7 +529,7 @@
 retrieveUrl = fileRetriever $ \f k p -> do
 	us <- getWebUrls k
 	unlessM (downloadUrl k p us f) $
-		error "failed to download content"
+		giveup "failed to download content"
 
 checkKeyUrl :: Git.Repo -> CheckPresent
 checkKeyUrl r k = do
diff --git a/Remote/GCrypt.hs b/Remote/GCrypt.hs
--- a/Remote/GCrypt.hs
+++ b/Remote/GCrypt.hs
@@ -164,16 +164,16 @@
 	othertransport = return ([], loc, AccessDirect)
 
 noCrypto :: Annex a
-noCrypto = error "cannot use gcrypt remote without encryption enabled"
+noCrypto = giveup "cannot use gcrypt remote without encryption enabled"
 
 unsupportedUrl :: a
-unsupportedUrl = error "using non-ssh remote repo url with gcrypt is not supported"
+unsupportedUrl = giveup "using non-ssh remote repo url with gcrypt is not supported"
 
 gCryptSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
 gCryptSetup mu _ c gc = go $ M.lookup "gitrepo" c
   where
 	remotename = fromJust (M.lookup "name" c)
-	go Nothing = error "Specify gitrepo="
+	go Nothing = giveup "Specify gitrepo="
 	go (Just gitrepo) = do
 		(c', _encsetup) <- encryptionSetup c gc
 		inRepo $ Git.Command.run 
@@ -200,7 +200,7 @@
 			]
 		g <- inRepo Git.Config.reRead
 		case Git.GCrypt.remoteRepoId g (Just remotename) of
-			Nothing -> error "unable to determine gcrypt-id of remote"
+			Nothing -> giveup "unable to determine gcrypt-id of remote"
 			Just gcryptid -> do
 				let u = genUUIDInNameSpace gCryptNameSpace gcryptid
 				if Just u == mu || isNothing mu
@@ -208,7 +208,7 @@
 						method <- setupRepo gcryptid =<< inRepo (Git.Construct.fromRemoteLocation gitrepo)
 						gitConfigSpecialRemote u c' "gcrypt" (fromAccessMethod method)
 						return (c', u)
-					else error $ "uuid mismatch; expected " ++ show mu ++ " but remote gitrepo has " ++ show u ++ " (" ++ show gcryptid ++ ")"
+					else giveup $ "uuid mismatch; expected " ++ show mu ++ " but remote gitrepo has " ++ show u ++ " (" ++ show gcryptid ++ ")"
 
 {- Sets up the gcrypt repository. The repository is either a local
  - repo, or it is accessed via rsync directly, or it is accessed over ssh
@@ -258,7 +258,7 @@
 			, Param rsyncurl
 			]
 		unless ok $
-			error "Failed to connect to remote to set it up."
+			giveup "Failed to connect to remote to set it up."
 		return AccessDirect
 
 	{-  Ask git-annex-shell to configure the repository as a gcrypt
@@ -337,7 +337,7 @@
 	| Git.repoIsSsh (repo r) = if accessShell r
 		then fileRetriever $ \f k p ->
 			unlessM (Ssh.rsyncHelper (Just p) =<< Ssh.rsyncParamsRemote False r Download k f Nothing) $
-				error "rsync failed"
+				giveup "rsync failed"
 		else fileRetriever $ Remote.Rsync.retrieve rsyncopts
 	| otherwise = unsupportedUrl
   where
diff --git a/Remote/Git.hs b/Remote/Git.hs
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -95,20 +95,20 @@
  -}
 gitSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
 gitSetup Nothing _ c _ = do
-	let location = fromMaybe (error "Specify location=url") $
+	let location = fromMaybe (giveup "Specify location=url") $
 		Url.parseURIRelaxed =<< M.lookup "location" c
 	g <- Annex.gitRepo
 	u <- case filter (\r -> Git.location r == Git.Url location) (Git.remotes g) of
 		[r] -> getRepoUUID r
-		[] -> error "could not find existing git remote with specified location"
-		_ -> error "found multiple git remotes with specified location"
+		[] -> giveup "could not find existing git remote with specified location"
+		_ -> giveup "found multiple git remotes with specified location"
 	return (c, u)
 gitSetup (Just u) _ c _ = do
 	inRepo $ Git.Command.run
 		[ Param "remote"
 		, Param "add"
-		, Param $ fromMaybe (error "no name") (M.lookup "name" c)
-		, Param $ fromMaybe (error "no location") (M.lookup "location" c)
+		, Param $ fromMaybe (giveup "no name") (M.lookup "name" c)
+		, Param $ fromMaybe (giveup "no location") (M.lookup "location" c)
 		]
 	return (c, u)
 
@@ -202,7 +202,7 @@
 tryGitConfigRead autoinit r 
 	| haveconfig r = return r -- already read
 	| Git.repoIsSsh r = store $ do
-		v <- Ssh.onRemote r (pipedconfig, return (Left $ error "configlist failed")) "configlist" [] configlistfields
+		v <- Ssh.onRemote r (pipedconfig, return (Left $ giveup "configlist failed")) "configlist" [] configlistfields
 		case v of
 			Right r'
 				| haveconfig r' -> return r'
@@ -321,7 +321,7 @@
 		showChecking r
 		ifM (Url.withUrlOptions $ \uo -> anyM (\u -> Url.checkBoth u (keySize key) uo) (keyUrls rmt key))
 			( return True
-			, error "not found"
+			, giveup "not found"
 			)
 	checkremote = Ssh.inAnnex r key
 	checklocal = guardUsable r (cantCheck r) $
@@ -357,7 +357,7 @@
 					logStatus key InfoMissing
 					Annex.Content.saveState True
 				return True
-	| Git.repoIsHttp (repo r) = error "dropping from http remote not supported"
+	| Git.repoIsHttp (repo r) = giveup "dropping from http remote not supported"
 	| otherwise = commitOnCleanup r $ Ssh.dropKey (repo r) key
 
 lockKey :: Remote -> Key -> (VerifiedCopy -> Annex r) -> Annex r
@@ -414,7 +414,7 @@
 					failedlock
 	| otherwise = failedlock
   where
-	failedlock = error "can't lock content"
+	failedlock = giveup "can't lock content"
 
 {- Tries to copy a key's content from a remote's annex to a file. -}
 copyFromRemote :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex (Bool, Verification)
@@ -444,7 +444,7 @@
 	| Git.repoIsSsh (repo r) = unVerified $ feedprogressback $ \p -> do
 		Ssh.rsyncHelper (Just (combineMeterUpdate meterupdate p))
 			=<< Ssh.rsyncParamsRemote False r Download key dest file
-	| otherwise = error "copying from non-ssh, non-http remote not supported"
+	| otherwise = giveup "copying from non-ssh, non-http remote not supported"
   where
 	{- Feed local rsync's progress info back to the remote,
 	 - by forking a feeder thread that runs
@@ -547,7 +547,7 @@
 			unlocked <- isDirect <||> versionSupportsUnlockedPointers
 			Ssh.rsyncHelper (Just meterupdate)
 				=<< Ssh.rsyncParamsRemote unlocked r Upload key object file
-	| otherwise = error "copying to non-ssh repo not supported"
+	| otherwise = giveup "copying to non-ssh repo not supported"
   where
 	copylocal Nothing = return False
 	copylocal (Just (object, checksuccess)) = do
diff --git a/Remote/Glacier.hs b/Remote/Glacier.hs
--- a/Remote/Glacier.hs
+++ b/Remote/Glacier.hs
@@ -146,7 +146,7 @@
 		, Param $ getVault $ config r
 		, Param $ archive r k
 		]
-	go Nothing = error "cannot retrieve from glacier"
+	go Nothing = giveup "cannot retrieve from glacier"
 	go (Just e) = do
 		let cmd = (proc "glacier" (toCommand params))
 			{ env = Just e
@@ -182,7 +182,7 @@
 	showChecking r
 	go =<< glacierEnv (config r) (gitconfig r) (uuid r)
   where
-	go Nothing = error "cannot check glacier"
+	go Nothing = giveup "cannot check glacier"
 	go (Just e) = do
 		{- glacier checkpresent outputs the archive name to stdout if
 		 - it's present. -}
@@ -190,7 +190,7 @@
 		let probablypresent = key2file k `elem` lines s
 		if probablypresent
 			then ifM (Annex.getFlag "trustglacier")
-				( return True, error untrusted )
+				( return True, giveup untrusted )
 			else return False
 
 	params = glacierParams (config r)
@@ -222,7 +222,7 @@
 glacierParams c params = datacenter:params
   where
 	datacenter = Param $ "--region=" ++
-		fromMaybe (error "Missing datacenter configuration")
+		fromMaybe (giveup "Missing datacenter configuration")
 			(M.lookup "datacenter" c)
 
 glacierEnv :: RemoteConfig -> RemoteGitConfig -> UUID -> Annex (Maybe [(String, String)])
@@ -239,7 +239,7 @@
 	(uk, pk) = credPairEnvironment creds
 
 getVault :: RemoteConfig -> Vault
-getVault = fromMaybe (error "Missing vault configuration") 
+getVault = fromMaybe (giveup "Missing vault configuration") 
 	. M.lookup "vault"
 
 archive :: Remote -> Key -> Archive
@@ -249,7 +249,7 @@
 
 genVault :: RemoteConfig -> RemoteGitConfig -> UUID -> Annex ()
 genVault c gc u = unlessM (runGlacier c gc u params) $
-	error "Failed creating glacier vault."
+	giveup "Failed creating glacier vault."
   where
 	params = 
 		[ Param "vault"
@@ -312,7 +312,7 @@
 checkSaneGlacierCommand :: IO ()
 checkSaneGlacierCommand = 
 	whenM ((Nothing /=) <$> catchMaybeIO shouldfail) $
-		error wrongcmd
+		giveup wrongcmd
   where
 	test = proc "glacier" ["--compatibility-test-git-annex"]
 	shouldfail = withQuietOutput createProcessSuccess test
diff --git a/Remote/Helper/Chunked.hs b/Remote/Helper/Chunked.hs
--- a/Remote/Helper/Chunked.hs
+++ b/Remote/Helper/Chunked.hs
@@ -59,7 +59,7 @@
 		Just size
 			| size == 0 -> NoChunks
 			| size > 0 -> c (fromInteger size)
-		_ -> error $ "bad configuration " ++ f ++ "=" ++ v
+		_ -> giveup $ "bad configuration " ++ f ++ "=" ++ v
 
 -- An infinite stream of chunk keys, starting from chunk 1.
 newtype ChunkKeyStream = ChunkKeyStream [Key]
@@ -250,9 +250,9 @@
 		let ls' = maybe ls (setupResume ls) currsize
 		if any null ls'
 			then return True -- dest is already complete
-			else firstavail currsize ls' `catchNonAsync` giveup
+			else firstavail currsize ls' `catchNonAsync` unable
 
-	giveup e = do
+	unable e = do
 		warning (show e)
 		return False
 
@@ -273,10 +273,10 @@
 						let sz = toBytesProcessed $
 							fromMaybe 0 $ keyChunkSize k
 						getrest p h sz sz ks
-							`catchNonAsync` giveup
+							`catchNonAsync` unable
 			case v of
 				Left e
-					| null ls -> giveup e
+					| null ls -> unable e
 					| otherwise -> firstavail currsize ls
 				Right r -> return r
 
@@ -286,7 +286,7 @@
 		liftIO $ p' zeroBytesProcessed
 		ifM (retriever (encryptor k) p' $ tosink (Just h) p')
 			( getrest p h sz (addBytesProcessed bytesprocessed sz) ks
-			, giveup "chunk retrieval failed"
+			, unable "chunk retrieval failed"
 			)
 
 	getunchunked = retriever (encryptor basek) basep $ tosink Nothing basep
diff --git a/Remote/Helper/Encryptable.hs b/Remote/Helper/Encryptable.hs
--- a/Remote/Helper/Encryptable.hs
+++ b/Remote/Helper/Encryptable.hs
@@ -66,14 +66,14 @@
 			encsetup $ genEncryptedCipher cmd (c, gc) key Hybrid
 		Just "pubkey" -> encsetup $ genEncryptedCipher cmd (c, gc) key PubKey
 		Just "sharedpubkey" -> encsetup $ genSharedPubKeyCipher cmd key
-		_ -> error $ "Specify " ++ intercalate " or "
+		_ -> giveup $ "Specify " ++ intercalate " or "
 			(map ("encryption=" ++)
 				["none","shared","hybrid","pubkey", "sharedpubkey"])
 			++ "."
-	key = fromMaybe (error "Specifiy keyid=...") $ M.lookup "keyid" c
+	key = fromMaybe (giveup "Specifiy keyid=...") $ M.lookup "keyid" c
 	newkeys = maybe [] (\k -> [(True,k)]) (M.lookup "keyid+" c) ++
 		maybe [] (\k -> [(False,k)]) (M.lookup "keyid-" c)
-	cannotchange = error "Cannot set encryption type of existing remotes."
+	cannotchange = giveup "Cannot set encryption type of existing remotes."
 	-- Update an existing cipher if possible.
 	updateCipher cmd v = case v of
 		SharedCipher _ | maybe True (== "shared") encryption -> return (c', EncryptionIsSetup)
diff --git a/Remote/Helper/Http.hs b/Remote/Helper/Http.hs
--- a/Remote/Helper/Http.hs
+++ b/Remote/Helper/Http.hs
@@ -70,7 +70,7 @@
 -- meter as it goes.
 httpBodyRetriever :: FilePath -> MeterUpdate -> Response BodyReader -> IO ()
 httpBodyRetriever dest meterupdate resp
-	| responseStatus resp /= ok200 = error $ show $ responseStatus resp
+	| responseStatus resp /= ok200 = giveup $ show $ responseStatus resp
 	| otherwise = bracket (openBinaryFile dest WriteMode) hClose (go zeroBytesProcessed)
   where
 	reader = responseBody resp
diff --git a/Remote/Helper/Messages.hs b/Remote/Helper/Messages.hs
--- a/Remote/Helper/Messages.hs
+++ b/Remote/Helper/Messages.hs
@@ -29,7 +29,7 @@
 showChecking v = showAction $ "checking " ++ describe v
 
 cantCheck :: Describable a => a -> e
-cantCheck v = error $ "unable to check " ++ describe v
+cantCheck v = giveup $ "unable to check " ++ describe v
 
 showLocking :: Describable a => a -> Annex ()
 showLocking v = showAction $ "locking " ++ describe v
diff --git a/Remote/Helper/Ssh.hs b/Remote/Helper/Ssh.hs
--- a/Remote/Helper/Ssh.hs
+++ b/Remote/Helper/Ssh.hs
@@ -29,7 +29,7 @@
 toRepo :: Git.Repo -> RemoteGitConfig -> [CommandParam] -> Annex [CommandParam]
 toRepo r gc sshcmd = do
 	let opts = map Param $ remoteAnnexSshOptions gc
-	let host = fromMaybe (error "bad ssh url") $ Git.Url.hostuser r
+	let host = fromMaybe (giveup "bad ssh url") $ Git.Url.hostuser r
 	params <- sshOptions (host, Git.Url.port r) gc opts
 	return $ params ++ Param host : sshcmd
 
diff --git a/Remote/Hook.hs b/Remote/Hook.hs
--- a/Remote/Hook.hs
+++ b/Remote/Hook.hs
@@ -68,12 +68,12 @@
 			, checkUrl = Nothing
 			}
   where
-	hooktype = fromMaybe (error "missing hooktype") $ remoteAnnexHookType gc
+	hooktype = fromMaybe (giveup "missing hooktype") $ remoteAnnexHookType gc
 
 hookSetup :: Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
 hookSetup mu _ c gc = do
 	u <- maybe (liftIO genUUID) return mu
-	let hooktype = fromMaybe (error "Specify hooktype=") $
+	let hooktype = fromMaybe (giveup "Specify hooktype=") $
 		M.lookup "hooktype" c
 	(c', _encsetup) <- encryptionSetup c gc
 	gitConfigSpecialRemote u c' "hooktype" hooktype
@@ -129,7 +129,7 @@
 retrieve :: HookName -> Retriever
 retrieve h = fileRetriever $ \d k _p ->
 	unlessM (runHook h "retrieve" k (Just d) $ return True) $
-		error "failed to retrieve content"
+		giveup "failed to retrieve content"
 
 retrieveCheap :: HookName -> Key -> AssociatedFile -> FilePath -> Annex Bool
 retrieveCheap _ _ _ _ = return False
@@ -145,7 +145,7 @@
   where
 	action = "checkpresent"
 	findkey s = key2file k `elem` lines s
-	check Nothing = error $ action ++ " hook misconfigured"
+	check Nothing = giveup $ action ++ " hook misconfigured"
 	check (Just hook) = do
 		environ <- hookEnv action k Nothing
 		findkey <$> readProcessEnv "sh" ["-c", hook] environ
diff --git a/Remote/Rsync.hs b/Remote/Rsync.hs
--- a/Remote/Rsync.hs
+++ b/Remote/Rsync.hs
@@ -53,7 +53,7 @@
 gen r u c gc = do
 	cst <- remoteCost gc expensiveRemoteCost
 	(transport, url) <- rsyncTransport gc $
-		fromMaybe (error "missing rsyncurl") $ remoteAnnexRsyncUrl gc
+		fromMaybe (giveup "missing rsyncurl") $ remoteAnnexRsyncUrl gc
 	let o = genRsyncOpts c gc transport url
 	let islocal = rsyncUrlIsPath $ rsyncUrl o
 	return $ Just $ specialRemote' specialcfg c
@@ -127,7 +127,7 @@
 					(map Param $ loginopt ++ sshopts')
 			"rsh":rshopts -> return $ map Param $ "rsh" :
 				loginopt ++ rshopts
-			rsh -> error $ "Unknown Rsync transport: "
+			rsh -> giveup $ "Unknown Rsync transport: "
 				++ unwords rsh
 	| otherwise = return ([], url)
   where
@@ -141,7 +141,7 @@
 rsyncSetup mu _ c gc = do
 	u <- maybe (liftIO genUUID) return mu
 	-- verify configuration is sane
-	let url = fromMaybe (error "Specify rsyncurl=") $
+	let url = fromMaybe (giveup "Specify rsyncurl=") $
 		M.lookup "rsyncurl" c
 	(c', _encsetup) <- encryptionSetup c gc
 
@@ -188,7 +188,7 @@
 retrieve :: RsyncOpts -> FilePath -> Key -> MeterUpdate -> Annex ()
 retrieve o f k p = 
 	unlessM (rsyncRetrieve o k f (Just p)) $
-		error "rsync failed"
+		giveup "rsync failed"
 
 retrieveCheap :: RsyncOpts -> Key -> AssociatedFile -> FilePath -> Annex Bool
 retrieveCheap o k _af f = ifM (preseedTmp k f) ( rsyncRetrieve o k f Nothing , return False )
diff --git a/Remote/S3.hs b/Remote/S3.hs
--- a/Remote/S3.hs
+++ b/Remote/S3.hs
@@ -136,7 +136,7 @@
 		-- Ensure user enters a valid bucket name, since
 		-- this determines the name of the archive.org item.
 		let validbucket = replace " " "-" $
-			fromMaybe (error "specify bucket=") $
+			fromMaybe (giveup "specify bucket=") $
 				getBucketName c'
 		let archiveconfig = 
 			-- IA acdepts x-amz-* as an alias for x-archive-*
@@ -252,7 +252,7 @@
 		return False
 	Just geturl -> fileRetriever $ \f k p ->
 		unlessM (downloadUrl k p [geturl k] f) $
-			error "failed to download content"
+			giveup "failed to download content"
 
 retrieveCheap :: Key -> AssociatedFile -> FilePath -> Annex Bool
 retrieveCheap _ _ _ = return False
@@ -301,7 +301,7 @@
 checkKey r info Nothing k = case getpublicurl info of
 	Nothing -> do
 		warnMissingCredPairFor "S3" (AWS.creds $ uuid r)
-		error "No S3 credentials configured"
+		giveup "No S3 credentials configured"
 	Just geturl -> do
 		showChecking r
 		withUrlOptions $ checkBoth (geturl k) (keySize k)
@@ -415,7 +415,7 @@
 	Just h -> a h
 	Nothing -> do
 		warnMissingCredPairFor "S3" (AWS.creds u)
-		error "No S3 credentials configured"
+		giveup "No S3 credentials configured"
 
 withS3HandleMaybe :: RemoteConfig -> RemoteGitConfig -> UUID -> (Maybe S3Handle -> Annex a) -> Annex a
 withS3HandleMaybe c gc u a = do
@@ -437,7 +437,7 @@
 	{ S3.s3Port = port
 	, S3.s3RequestStyle = case M.lookup "requeststyle" c of
 		Just "path" -> S3.PathStyle
-		Just s -> error $ "bad S3 requeststyle value: " ++ s
+		Just s -> giveup $ "bad S3 requeststyle value: " ++ s
 		Nothing -> S3.s3RequestStyle cfg
 	}
   where
@@ -455,7 +455,7 @@
 	port = let s = fromJust $ M.lookup "port" c in
 		case reads s of
 		[(p, _)] -> p
-		_ -> error $ "bad S3 port value: " ++ s
+		_ -> giveup $ "bad S3 port value: " ++ s
 	cfg = S3.s3 proto endpoint False
 
 tryS3 :: Annex a -> Annex (Either S3.S3Error a)
@@ -475,7 +475,7 @@
 extractS3Info :: RemoteConfig -> Annex S3Info
 extractS3Info c = do
 	b <- maybe
-		(error "S3 bucket not configured")
+		(giveup "S3 bucket not configured")
 		(return . T.pack)
 		(getBucketName c)
 	let info = S3Info
diff --git a/Remote/Tahoe.hs b/Remote/Tahoe.hs
--- a/Remote/Tahoe.hs
+++ b/Remote/Tahoe.hs
@@ -109,7 +109,7 @@
   where
 	scsk = "shared-convergence-secret"
 	furlk = "introducer-furl"
-	missingfurl = error "Set TAHOE_FURL to the introducer furl to use."
+	missingfurl = giveup "Set TAHOE_FURL to the introducer furl to use."
 
 store :: UUID -> TahoeHandle -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
 store u hdl k _f _p = sendAnnex k noop $ \src ->
@@ -137,7 +137,7 @@
 			[ Param "--raw"
 			, Param cap
 			]
-		either error return v
+		either giveup return v
 
 defaultTahoeConfigDir :: UUID -> IO TahoeConfigDir
 defaultTahoeConfigDir u = do
@@ -147,7 +147,7 @@
 tahoeConfigure :: TahoeConfigDir -> IntroducerFurl -> Maybe SharedConvergenceSecret -> IO SharedConvergenceSecret
 tahoeConfigure configdir furl mscs = do
 	unlessM (createClient configdir furl) $
-		error "tahoe create-client failed"
+		giveup "tahoe create-client failed"
 	maybe noop (writeSharedConvergenceSecret configdir) mscs
 	startTahoeDaemon configdir
 	getSharedConvergenceSecret configdir
@@ -173,7 +173,7 @@
   where
 	f = convergenceFile configdir
 	go n
-		| n == 0 = error $ "tahoe did not write " ++ f ++ " after 1 minute. Perhaps the daemon failed to start?"
+		| n == 0 = giveup $ "tahoe did not write " ++ f ++ " after 1 minute. Perhaps the daemon failed to start?"
 		| otherwise = do
 			v <- catchMaybeIO (readFile f)
 			case v of
diff --git a/Remote/Web.hs b/Remote/Web.hs
--- a/Remote/Web.hs
+++ b/Remote/Web.hs
@@ -100,7 +100,7 @@
 	us <- getWebUrls key
 	if null us
 		then return False
-		else either error return =<< checkKey' key us
+		else either giveup return =<< checkKey' key us
 checkKey' :: Key -> [URLString] -> Annex (Either String Bool)
 checkKey' key us = firsthit us (Right False) $ \u -> do
 	let (u', downloader) = getDownloader u
diff --git a/Remote/WebDAV.hs b/Remote/WebDAV.hs
--- a/Remote/WebDAV.hs
+++ b/Remote/WebDAV.hs
@@ -85,7 +85,7 @@
 webdavSetup mu mcreds c gc = do
 	u <- maybe (liftIO genUUID) return mu
 	url <- case M.lookup "url" c of
-		Nothing -> error "Specify url="
+		Nothing -> giveup "Specify url="
 		Just url -> return url
 	(c', encsetup) <- encryptionSetup c gc
 	creds <- maybe (getCreds c' gc u) (return . Just) mcreds
@@ -122,7 +122,7 @@
 retrieveCheap _ _ _ = return False
 
 retrieve :: ChunkConfig -> Maybe DavHandle -> Retriever
-retrieve _ Nothing = error "unable to connect"
+retrieve _ Nothing = giveup "unable to connect"
 retrieve (LegacyChunks _) (Just dav) = retrieveLegacyChunked dav
 retrieve _ (Just dav) = fileRetriever $ \d k p -> liftIO $
 	goDAV dav $
@@ -147,7 +147,7 @@
 					_ -> return False
 
 checkKey :: Remote -> ChunkConfig -> Maybe DavHandle -> CheckPresent
-checkKey r _ Nothing _ = error $ name r ++ " not configured"
+checkKey r _ Nothing _ = giveup $ name r ++ " not configured"
 checkKey r chunkconfig (Just dav) k = do
 	showChecking r
 	case chunkconfig of
@@ -155,7 +155,7 @@
 		_ -> do
 			v <- liftIO $ goDAV dav $
 				existsDAV (keyLocation k)
-			either error return v
+			either giveup return v
 
 configUrl :: Remote -> Maybe URLString
 configUrl r = fixup <$> M.lookup "url" (config r)
diff --git a/Upgrade.hs b/Upgrade.hs
--- a/Upgrade.hs
+++ b/Upgrade.hs
@@ -21,7 +21,7 @@
 import qualified Upgrade.V5
 
 checkUpgrade :: Version -> Annex ()
-checkUpgrade = maybe noop error <=< needsUpgrade
+checkUpgrade = maybe noop giveup <=< needsUpgrade
 
 needsUpgrade :: Version -> Annex (Maybe String)
 needsUpgrade v
@@ -49,8 +49,8 @@
 	go (Just "0") = Upgrade.V0.upgrade
 	go (Just "1") = Upgrade.V1.upgrade
 #else
-	go (Just "0") = error "upgrade from v0 on Windows not supported"
-	go (Just "1") = error "upgrade from v1 on Windows not supported"
+	go (Just "0") = giveup "upgrade from v0 on Windows not supported"
+	go (Just "1") = giveup "upgrade from v1 on Windows not supported"
 #endif
 	go (Just "2") = Upgrade.V2.upgrade
 	go (Just "3") = Upgrade.V3.upgrade automatic
diff --git a/Utility/Daemon.hs b/Utility/Daemon.hs
--- a/Utility/Daemon.hs
+++ b/Utility/Daemon.hs
@@ -111,7 +111,7 @@
 #endif
 
 alreadyRunning :: IO ()
-alreadyRunning = error "Daemon is already running."
+alreadyRunning = giveup "Daemon is already running."
 
 {- Checks if the daemon is running, by checking that the pid file
  - is locked by the same process that is listed in the pid file.
@@ -135,7 +135,7 @@
 	check _ Nothing = Nothing
 	check (Just (pid, _)) (Just pid')
 		| pid == pid' = Just pid
-		| otherwise = error $
+		| otherwise = giveup $
 			"stale pid in " ++ pidfile ++ 
 			" (got " ++ show pid' ++ 
 			"; expected " ++ show pid ++ " )"
diff --git a/Utility/DirWatcher/FSEvents.hs b/Utility/DirWatcher/FSEvents.hs
--- a/Utility/DirWatcher/FSEvents.hs
+++ b/Utility/DirWatcher/FSEvents.hs
@@ -17,7 +17,7 @@
 watchDir :: FilePath -> (FilePath -> Bool) -> Bool -> WatchHooks -> IO EventStream
 watchDir dir ignored scanevents hooks = do
 	unlessM fileLevelEventsSupported $
-		error "Need at least OSX 10.7.0 for file-level FSEvents"
+		giveup "Need at least OSX 10.7.0 for file-level FSEvents"
 	scan dir
 	eventStreamCreate [dir] 1.0 True True True dispatch
   where
diff --git a/Utility/DirWatcher/INotify.hs b/Utility/DirWatcher/INotify.hs
--- a/Utility/DirWatcher/INotify.hs
+++ b/Utility/DirWatcher/INotify.hs
@@ -152,7 +152,7 @@
 		-- disk full error.
 		| isFullError e =
 			case errHook hooks of
-				Nothing -> error $ "failed to add inotify watch on directory " ++ dir ++ " (" ++ show e ++ ")"
+				Nothing -> giveup $ "failed to add inotify watch on directory " ++ dir ++ " (" ++ show e ++ ")"
 				Just hook -> tooManyWatches hook dir
 		-- The directory could have been deleted.
 		| isDoesNotExistError e = return ()
diff --git a/Utility/Exception.hs b/Utility/Exception.hs
--- a/Utility/Exception.hs
+++ b/Utility/Exception.hs
@@ -1,6 +1,6 @@
 {- Simple IO exception handling (and some more)
  -
- - Copyright 2011-2015 Joey Hess <id@joeyh.name>
+ - Copyright 2011-2016 Joey Hess <id@joeyh.name>
  -
  - License: BSD-2-clause
  -}
@@ -10,6 +10,7 @@
 
 module Utility.Exception (
 	module X,
+	giveup,
 	catchBoolIO,
 	catchMaybeIO,
 	catchDefaultIO,
@@ -39,6 +40,21 @@
 import GHC.IO.Exception (IOErrorType(..))
 
 import Utility.Data
+
+{- Like error, this throws an exception. Unlike error, if this exception
+ - is not caught, it won't generate a backtrace. So use this for situations
+ - where there's a problem that the user is excpected to see in some
+ - circumstances. -}
+giveup :: [Char] -> a
+#ifdef MIN_VERSION_base
+#if MIN_VERSION_base(4,9,0)
+giveup = errorWithoutStackTrace
+#else
+giveup = error
+#endif
+#else
+giveup = error
+#endif
 
 {- Catches IO errors and returns a Bool -}
 catchBoolIO :: MonadCatch m => m Bool -> m Bool
diff --git a/Utility/Glob.hs b/Utility/Glob.hs
--- a/Utility/Glob.hs
+++ b/Utility/Glob.hs
@@ -12,6 +12,8 @@
 	matchGlob
 ) where
 
+import Utility.Exception
+
 import System.Path.WildMatch
 
 import "regex-tdfa" Text.Regex.TDFA
@@ -26,7 +28,7 @@
 compileGlob glob globcase = Glob $
 	case compile (defaultCompOpt {caseSensitive = casesentitive}) defaultExecOpt regex of
 		Right r -> r
-		Left _ -> error $ "failed to compile regex: " ++ regex
+		Left _ -> giveup $ "failed to compile regex: " ++ regex
   where
 	regex = '^':wildToRegex glob
 	casesentitive = case globcase of
diff --git a/Utility/Gpg.hs b/Utility/Gpg.hs
--- a/Utility/Gpg.hs
+++ b/Utility/Gpg.hs
@@ -253,7 +253,7 @@
 			then s
 			else shortread len
 
-	shortread got = error $ unwords
+	shortread got = giveup $ unwords
 		[ "Not enough bytes returned from gpg", show params
 		, "(got", show got, "; expected", show expectedlength, ")"
 		]
diff --git a/Utility/LockFile/PidLock.hs b/Utility/LockFile/PidLock.hs
--- a/Utility/LockFile/PidLock.hs
+++ b/Utility/LockFile/PidLock.hs
@@ -210,7 +210,7 @@
 			=<< tryLock lockfile
 		| otherwise = do
 			hPutStrLn stderr $ show timeout ++ " second timeout exceeded while waiting for pid lock file " ++ lockfile
-			error $ "Gave up waiting for possibly stale pid lock file " ++ lockfile
+			giveup $ "Gave up waiting for possibly stale pid lock file " ++ lockfile
 
 dropLock :: LockHandle -> IO ()
 dropLock (LockHandle lockfile _ sidelock) = do
diff --git a/Utility/Quvi.hs b/Utility/Quvi.hs
--- a/Utility/Quvi.hs
+++ b/Utility/Quvi.hs
@@ -79,8 +79,8 @@
 forceQuery v ps url = query' v ps url `catchNonAsync` onerr
   where
 	onerr e = ifM (inPath "quvi")
-		( error ("quvi failed: " ++ show e)
-		, error "quvi is not installed"
+		( giveup ("quvi failed: " ++ show e)
+		, giveup "quvi is not installed"
 		)
 
 {- Returns Nothing if the page is not a video page, or quvi is not
diff --git a/Utility/UserInfo.hs b/Utility/UserInfo.hs
--- a/Utility/UserInfo.hs
+++ b/Utility/UserInfo.hs
@@ -16,6 +16,7 @@
 
 import Utility.Env
 import Utility.Data
+import Utility.Exception
 
 import System.PosixCompat
 import Control.Applicative
@@ -25,7 +26,7 @@
  -
  - getpwent will fail on LDAP or NIS, so use HOME if set. -}
 myHomeDir :: IO FilePath
-myHomeDir = either error return =<< myVal env homeDirectory
+myHomeDir = either giveup return =<< myVal env homeDirectory
   where
 #ifndef mingw32_HOST_OS
 	env = ["HOME"]
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: 6.20161111
+Version: 6.20161118
 Cabal-Version: >= 1.8
 License: GPL-3
 Maintainer: Joey Hess <id@joeyh.name>
@@ -359,10 +359,7 @@
    old-locale,
    esqueleto,
    persistent-sqlite, 
-   -- Old version needed due to
-   -- https://github.com/prowdsponsor/esqueleto/issues/137
-   -- and also temporarily to make ghc 8 builds work
-   persistent (< 2.5),
+   persistent,
    persistent-template,
    aeson,
    unordered-containers,
