diff --git a/src/Tahoe/Download.hs b/src/Tahoe/Download.hs
index b899710acbc6f52e8ea1a17067af5e5db6a529d5..b2b394fbc42459c39b9eeba03661f6f342e634e6 100644
--- a/src/Tahoe/Download.hs
+++ b/src/Tahoe/Download.hs
@@ -1,21 +1,14 @@
 module Tahoe.Download (DownloadError (..), download) where
 
-import Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as LB
 import Data.Either (rights)
 import qualified Data.Map.Strict as Map
-
--- import qualified Data.Set as Set
-import Data.Word (Word8)
+import qualified Data.Set as Set
+import qualified Data.Text as T
+import Data.Text.Encoding (encodeUtf8)
 import Tahoe.CHK.Capability (Reader (..), Verifier (..))
-import Tahoe.CHK.Server (StorageServerAnnouncement (..), StorageServerID)
-import Tahoe.CHK.Types (StorageIndex)
-
-newtype ShareNum = ShareNum Word8 deriving (Eq, Ord, Show)
-
--- {- | A map from share numbers to servers where the corresponding shares have
---  recently been observed.
--- -}
--- type ShareMap = Map.Map ShareNum (Set.Set StorageServerID)
+import Tahoe.CHK.Server (StorageServer (..), StorageServerAnnouncement (..), StorageServerID, URL)
+import Tahoe.CHK.Types (ShareNum, StorageIndex)
 
 {- | An unrecoverable problem arose while attempting to download and/or read
  some application data.
@@ -25,6 +18,12 @@ data DownloadError
       NoConfiguredServers
     | -- | Across all of the configured servers, none were actually connectable.
       NoReachableServers
+    | -- | Across all of the configured servers, fewer than the required
+      -- number of shares were found.
+      NotEnoughShares
+        { notEnoughSharesNeeded :: Int
+        , notEnoughSharesFound :: Int
+        }
     deriving (Eq, Ord, Show)
 
 {- | Recover the application data associated with a given capability from the
@@ -36,21 +35,32 @@ download ::
     Map.Map StorageServerID StorageServerAnnouncement ->
     -- | The read capability for the application data.
     Reader ->
+    -- | Get functions for interacting with a server given its URL.
+    (URL -> IO (Maybe StorageServer)) ->
     -- | Either a description of how the recovery failed or the recovered
     -- application data.
-    IO (Either DownloadError ByteString)
-download servers Reader{verifier = Verifier{..}} =
+    IO (Either DownloadError LB.ByteString)
+download servers Reader{verifier = Verifier{..}} openServer =
     case Map.toList servers of
         [] -> pure . Left $ NoConfiguredServers
         serverList -> do
             -- Ask each server for all shares it has.
-            discovered <- rights <$> mapM discoverOnce serverList
-            case discovered of
-                [] -> pure $ Left NoReachableServers
-                _ -> pure $ Right ""
+            discovered <- rights <$> mapM discoverOnce serverList :: IO [(StorageServerID, Set.Set ShareNum)]
+            if null discovered
+                then pure $ Left NoReachableServers
+                else
+                    if (fromIntegral required >=) . countShares $ discovered
+                        then pure $ Left NotEnoughShares{notEnoughSharesNeeded = fromIntegral required, notEnoughSharesFound = countShares discovered}
+                        else pure . Right . LB.fromStrict . encodeUtf8 . T.pack . show $ discovered
   where
+    -- Figure the total number of shares reported by all of the servers we
+    -- asked.
+    countShares = sum . map (Set.size . snd)
+
+    -- Ask one server which shares it has related to the storage index in
+    -- question.
     discoverOnce (sid, sann) = do
-        sharenums <- discoverShares storageIndex sann
+        sharenums <- discoverShares storageIndex sann openServer
         pure $ case sharenums of
             Left e -> Left e
             Right shnums -> Right (sid, shnums)
@@ -61,6 +71,7 @@ download servers Reader{verifier = Verifier{..}} =
 data DiscoverError
     = -- | An announcement did not include a location for a connection attempt.
       StorageServerLocationUnknown
+    | StorageServerUnreachable
     deriving (Eq, Ord, Show)
 
 -- | Identify which servers claim to have some data at some index.
@@ -71,9 +82,16 @@ discoverShares ::
     -- have the data because local configuration suggests the data might have
     -- been uploaded to them in the past.
     StorageServerAnnouncement ->
+    -- | A function to connect to a server.
+    (URL -> IO (Maybe StorageServer)) ->
     -- | The share numbers the server claims to have.
-    IO (Either DiscoverError [ShareNum])
-discoverShares _storageIndex ann
-    | isNothing $ storageServerAnnouncementFURL ann =
-        pure $ Left StorageServerLocationUnknown
-    | otherwise = pure $ Right []
+    IO (Either DiscoverError (Set.Set ShareNum))
+discoverShares storageIndex ann openServer =
+    case storageServerAnnouncementFURL ann of
+        Nothing -> pure $ Left StorageServerLocationUnknown
+        Just url -> do
+            server <- openServer url
+            case server of
+                Nothing -> pure $ Left StorageServerUnreachable
+                Just StorageServer{storageServerGetBuckets} ->
+                    Right <$> storageServerGetBuckets storageIndex
diff --git a/test/Spec.hs b/test/Spec.hs
index 450c9bc88b01ae4ede47b7c1bf8b39669a886bb7..42c875383dbcc729d0713e72d834fc502d351578 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -7,8 +7,9 @@ import qualified Data.Map.Strict as Map
 import Data.Word (Word16)
 import System.IO (hSetEncoding, stderr, stdout, utf8)
 import Tahoe.CHK.Capability (Reader (..), Verifier (..))
-import Tahoe.CHK.Server (StorageServerAnnouncement (..))
+import Tahoe.CHK.Server (StorageServer (..), StorageServerAnnouncement (..))
 import Tahoe.Download (DownloadError (..), download)
+import Tahoe.Server (memoryStorageServer)
 import Test.Tasty (TestTree, defaultMain, testGroup)
 import Test.Tasty.HUnit (assertEqual, testCase)
 
@@ -19,7 +20,7 @@ tests =
         [ testCase "no configured servers" $ do
             -- If there are no servers then we can't possibly get enough
             -- shares to recover the application data.
-            result <- liftIO $ download mempty (trivialCap 1 1)
+            result <- liftIO $ download mempty (trivialCap 1 1) noServers
             assertEqual
                 "download should fail with no servers"
                 (Left NoConfiguredServers)
@@ -27,7 +28,7 @@ tests =
         , testCase "no reachable servers" $ do
             -- If we can't contact any configured server then we can't
             -- possibly get enough shares to recover the application data.
-            let servers =
+            let anns =
                     Map.fromList
                         [
                             ( "v0-abc123"
@@ -39,12 +40,48 @@ tests =
                             )
                         ]
 
-            result <- liftIO $ download servers (trivialCap 1 1)
+            result <- liftIO $ download anns (trivialCap 1 1) noServers
             assertEqual
                 "download should fail with no reachable servers"
                 (Left NoReachableServers)
                 result
+        , testCase "not enough shares" $ do
+            -- If we can't recover enough shares from the configured servers
+            -- then we can't possibly get enough shares to recover the
+            -- application data.
+            let anns =
+                    Map.fromList
+                        [
+                            ( "v0-abc123"
+                            , StorageServerAnnouncement
+                                { storageServerAnnouncementFURL = Just "somewhere"
+                                , storageServerAnnouncementNick = Just "abc123"
+                                , storageServerAnnouncementPermutationSeed = Nothing
+                                }
+                            )
+                        ]
+                cap = trivialCap 3 3
+
+            -- Two shares exist.
+            server <- memoryStorageServer
+            storageServerWrite server (storageIndex . verifier $ cap) 0 0 "Hello world"
+            storageServerWrite server (storageIndex . verifier $ cap) 1 0 "Hello world"
+
+            -- Make the server reachable.
+            let openServer furl =
+                    if furl == "somewhere"
+                        then pure . pure $ server
+                        else pure Nothing
+
+            -- Try to download the cap which requires three shares to reconstruct.
+            result <- liftIO $ download anns cap openServer
+            assertEqual
+                "download should fail with not enough shares"
+                (Left NotEnoughShares{notEnoughSharesNeeded = 3, notEnoughSharesFound = 2})
+                result
         ]
+  where
+    noServers _ = pure Nothing
 
 trivialCap :: Word16 -> Word16 -> Reader
 trivialCap required total = Reader{..}