diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,7 @@
 Changelog for Shake
 
+0.15.10
+    #465, fix phony names which clash with directories
 0.15.9
     Documentation tweaks
     Optimise the thread pool
diff --git a/shake.cabal b/shake.cabal
--- a/shake.cabal
+++ b/shake.cabal
@@ -1,7 +1,7 @@
 cabal-version:      >= 1.10
 build-type:         Simple
 name:               shake
-version:            0.15.9
+version:            0.15.10
 license:            BSD3
 license-file:       LICENSE
 category:           Development, Shake
diff --git a/src/Development/Shake/FileInfo.hs b/src/Development/Shake/FileInfo.hs
--- a/src/Development/Shake/FileInfo.hs
+++ b/src/Development/Shake/FileInfo.hs
@@ -3,7 +3,7 @@
 module Development.Shake.FileInfo(
     FileInfo, fileInfoEq, fileInfoNeq,
     FileSize, ModTime, FileHash,
-    getFileHash, getFileInfo
+    getFileHash, getFileInfo, getFileInfoNoDirErr
     ) where
 
 import Control.Exception.Extra
@@ -83,10 +83,17 @@
 
 
 getFileInfo :: BSU -> IO (Maybe (ModTime, FileSize))
+getFileInfo = getFileInfoEx True
 
+getFileInfoNoDirErr :: BSU -> IO (Maybe (ModTime, FileSize))
+getFileInfoNoDirErr = getFileInfoEx False
+
+
+getFileInfoEx :: Bool -> BSU -> IO (Maybe (ModTime, FileSize))
+
 #if defined(PORTABLE)
 -- Portable fallback
-getFileInfo x = handleBool isDoesNotExistError (const $ return Nothing) $ do
+getFileInfoEx direrr x = handleBool isDoesNotExistError (const $ return Nothing) $ do
     let file = unpackU x
     time <- getModificationTime file
     size <- withFile file ReadMode hFileSize
@@ -102,14 +109,14 @@
 
 #elif defined(mingw32_HOST_OS)
 -- Directly against the Win32 API, twice as fast as the portable version
-getFileInfo x = BS.useAsCString (unpackU_ x) $ \file ->
+getFileInfoEx direrr x = BS.useAsCString (unpackU_ x) $ \file ->
     alloca_WIN32_FILE_ATTRIBUTE_DATA $ \fad -> do
         res <- c_GetFileAttributesExA file 0 fad
         code <- peekFileAttributes fad
         let peek = do
                 code <- peekFileAttributes fad
                 if testBit code 4 then
-                    errorDirectoryNotFile $ unpackU x
+                    (if direrr then errorDirectoryNotFile $ unpackU x else return Nothing)
                  else
                     join $ liftM2 result (peekLastWriteTimeLow fad) (peekFileSizeLow fad)
         if res then
@@ -150,10 +157,10 @@
 
 #else
 -- Unix version
-getFileInfo x = handleBool isDoesNotExistError (const $ return Nothing) $ do
+getFileInfoEx direrr x = handleBool isDoesNotExistError (const $ return Nothing) $ do
     s <- getFileStatus $ unpackU_ x
     if isDirectory s then
-        errorDirectoryNotFile $ unpackU x
+        (if direrr then errorDirectoryNotFile $ unpackU x else return Nothing)
      else
         result (extractFileTime s) (fromIntegral $ fileSize s)
 
diff --git a/src/Development/Shake/Rules/File.hs b/src/Development/Shake/Rules/File.hs
--- a/src/Development/Shake/Rules/File.hs
+++ b/src/Development/Shake/Rules/File.hs
@@ -61,7 +61,7 @@
 
 instance Rule FileQ FileA where
     storedValue ShakeOptions{shakeChange=c} (FileQ x) = do
-        res <- getFileInfo x
+        res <- getFileInfoNoDirErr x
         case res of
             Nothing -> return Nothing
             Just (time,size) | c == ChangeModtime -> return $ Just $ FileA time size fileInfoNeq
@@ -79,6 +79,18 @@
         where bool b = if b then EqualCheap else NotEqual
 
 
+storedValueErrDir :: ShakeOptions -> FileQ -> IO (Maybe FileA)
+storedValueErrDir ShakeOptions{shakeChange=c} (FileQ x) = do
+    res <- getFileInfo x
+    case res of
+        Nothing -> return Nothing
+        Just (time,size) | c == ChangeModtime -> return $ Just $ FileA time size fileInfoNeq
+        Just (time,size) -> do
+            hash <- unsafeInterleaveIO $ getFileHash x
+            return $ Just $ FileA (if c == ChangeDigest then fileInfoNeq else time) size hash
+
+
+
 -- | Arguments: options; is the file an input; a message for failure if the file does not exist; filename
 storedValueError :: ShakeOptions -> Bool -> String -> FileQ -> IO FileA
 {-
@@ -87,7 +99,7 @@
         whenM (isNothing <$> (storedValue opts x :: IO (Maybe FileA))) $ error $ msg ++ "\n  " ++ unpackU (fromFileQ x)
     return $ FileA fileInfoEq fileInfoEq fileInfoEq
 -}
-storedValueError opts input msg x = fromMaybe def <$> storedValue opts2 x
+storedValueError opts input msg x = fromMaybe def <$> storedValueErrDir opts2 x
     where def = if shakeCreationCheck opts || input then error err else FileA fileInfoNeq fileInfoNeq fileInfoNeq
           err = msg ++ "\n  " ++ unpackU (fromFileQ x)
           opts2 = if not input && shakeChange opts == ChangeModtimeAndDigestInput then opts{shakeChange=ChangeModtime} else opts
@@ -144,7 +156,7 @@
 neededCheck :: [BSU] -> Action ()
 neededCheck (map (packU_ . filepathNormalise . unpackU_) -> xs) = do
     opts <- getShakeOptions
-    pre <- liftIO $ mapM (storedValue opts . FileQ) xs
+    pre <- liftIO $ mapM (storedValueErrDir opts . FileQ) xs
     post <- apply $ map FileQ xs :: Action [FileA]
     let bad = [ (x, if isJust a then "File change" else "File created")
               | (x, a, b) <- zip3 xs pre post, maybe NotEqual (\a -> equalValue opts (FileQ x) a b) a == NotEqual]
diff --git a/src/Test/Basic.hs b/src/Test/Basic.hs
--- a/src/Test/Basic.hs
+++ b/src/Test/Basic.hs
@@ -78,6 +78,9 @@
     obj "ids/out" %> \out -> do need =<< readFileLines (obj "ids/source"); writeFile' out ""
     obj "ids/*" %> \out -> do alwaysRerun; trace (takeFileName out); writeFile' out $ takeFileName out
 
+    phony (obj "foo") $ do
+        liftIO $ createDirectoryIfMissing True $ obj "foo"
+
 test build obj = do
     writeFile (obj "A.txt") "AAA"
     writeFile (obj "B.txt") "BBB"
@@ -177,5 +180,8 @@
     build ["ids/out","-j4"]
     -- if you collapse depends to [Id] then this ends up asking for the stale 'a'
     assertContents (obj ".log") "b"
+
+    build ["foo"]
+    build ["foo"]
 
     build [] -- should say "no want/action statements, nothing to do" (checked manually)
