diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,18 @@
 
 # Releases
 
+## Hakyll 4.16.6.0 (2025-02-18)
+
+- Do not crawl directories for which we do not have permissions 
+  with `Hakyll.Core.Util.File.getRecursiveContents`. This used to 
+  throw an exception.
+- Do not return broken symbolic links from
+  `Hakyll.Core.Util.File.getRecursiveContents`.  This used to cause
+  subsequent code to throw exceptions (e.g., when it attempts to
+  `getModificationTime`) (#1065) (Contribution by Wren Romano).
+- Ignore files in `dist-newstyle` and `.stack-work` directories, which
+  are Haskell build directories.
+
 ## Hakyll 4.16.5.0 (2025-01-11)
 
 - GHC 9.12 compatibility: bump `template-haskell` upper bound to include 2.23
diff --git a/hakyll.cabal b/hakyll.cabal
--- a/hakyll.cabal
+++ b/hakyll.cabal
@@ -1,5 +1,5 @@
 Name:    hakyll
-Version: 4.16.5.0
+Version: 4.16.6.0
 
 Synopsis: A static website compiler library
 Description:
@@ -99,7 +99,7 @@
 
 Source-Repository head
   Type:     git
-  Location: git://github.com/jaspervdj/hakyll.git
+  Location: https://github.com/jaspervdj/hakyll.git
 
 Flag previewServer
   Description: Include the preview server
diff --git a/lib/Hakyll/Core/Configuration.hs b/lib/Hakyll/Core/Configuration.hs
--- a/lib/Hakyll/Core/Configuration.hs
+++ b/lib/Hakyll/Core/Configuration.hs
@@ -135,7 +135,9 @@
 -- | Check if a file should be ignored
 shouldIgnoreFile :: Configuration -> FilePath -> IO Bool
 shouldIgnoreFile conf path = orM
-    [ inDir (destinationDirectory conf)
+    [ inDir ("dist-newstyle") -- build directory for cabal-install
+    , inDir (".stack-work")   -- build directory for stack
+    , inDir (destinationDirectory conf)
     , inDir (storeDirectory conf)
     , inDir (tmpDirectory conf)
     , return (ignoreFile conf path')
diff --git a/lib/Hakyll/Core/Util/File.hs b/lib/Hakyll/Core/Util/File.hs
--- a/lib/Hakyll/Core/Util/File.hs
+++ b/lib/Hakyll/Core/Util/File.hs
@@ -6,14 +6,17 @@
     ( makeDirectories
     , getRecursiveContents
     , removeDirectory
+    , withPermissions
     ) where
 
 
 --------------------------------------------------------------------------------
+import           Control.Exception   (throw)
 import           Control.Monad       (filterM, forM)
-import           System.Directory    (createDirectoryIfMissing,
+import           System.Directory    (createDirectoryIfMissing, doesPathExist,
                                       doesDirectoryExist, getDirectoryContents)
 import           System.FilePath     (takeDirectory, (</>))
+import           System.IO.Error     (catchIOError, isPermissionError)
 #ifndef mingw32_HOST_OS
 import           Control.Monad       (when)
 import           System.Directory    (removeDirectoryRecursive)
@@ -33,29 +36,41 @@
 
 --------------------------------------------------------------------------------
 -- | Get all contents of a directory.
+--
+-- If a directory is encountered for which you do not have
+-- permission, the directory will be skipped instead of
+-- an exception being thrown.
+--
+-- If a dangling\/broken symbolic link is encountered, then it will
+-- be skipped (since returning it may cause callers to throw exceptions).
 getRecursiveContents :: (FilePath -> IO Bool)  -- ^ Ignore this file/directory
                      -> FilePath               -- ^ Directory to search
-                     -> IO [FilePath]          -- ^ List of files found
+                     -> IO [FilePath]          -- ^ List of files found for which you have permissions
 getRecursiveContents ignore top = go ""
   where
     isProper x
         | x `elem` [".", ".."] = return False
         | otherwise            = not <$> ignore x
 
-    go dir     = do
-        dirExists <- doesDirectoryExist (top </> dir)
+    getProperDirectoryContents absDir =
+        filterM isProper =<< withPermissions (getDirectoryContents absDir) []
+
+    go relDir = do
+        let absDir = top </> relDir
+        dirExists <- doesDirectoryExist absDir
         if not dirExists
             then return []
             else do
-                names <- filterM isProper =<< getDirectoryContents (top </> dir)
-                paths <- forM names $ \name -> do
-                    let rel = dir </> name
-                    isDirectory <- doesDirectoryExist (top </> rel)
+                names <- getProperDirectoryContents absDir
+                fmap concat . forM names $ \name -> do
+                    let relPath = relDir </> name
+                        absPath = top </> relPath
+                    isDirectory <- doesDirectoryExist absPath
                     if isDirectory
-                        then go rel
-                        else return [rel]
-
-                return $ concat paths
+                        then go relPath
+                        else do
+                            pathExists <- doesPathExist absPath
+                            return $ if pathExists then [relPath] else []
 
 
 --------------------------------------------------------------------------------
@@ -85,3 +100,15 @@
     | i == 1    = x
     | otherwise = catch x $ \(_::SomeException) -> threadDelay 100 >> retryWithDelay (i-1) x
 #endif
+
+--------------------------------------------------------------------------------
+-- | Perform an IO action, catching any permission errors and returning
+--   a default value in their place.  All other exceptions are rethrown.
+withPermissions :: IO a
+                -> a  -- ^ Default value to return in case of a permission error
+                -> IO a
+withPermissions act onError
+    = act `catchIOError` \e ->
+        if isPermissionError e
+            then pure onError
+            else throw e
