diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -33,26 +33,27 @@
     λ hfmt --help
     hfmt - format Haskell programs
 
-    Usage: hfmt.exe [-d|--print-diffs] [-s|--print-sources] [-l|--print-paths]
-                    [-w|--write-sources] [PATH]
-      Operates on Haskell source files, reformatting them by applying suggestions
-      from HLint, hindent, and stylish-haskell. Inspired by the gofmt utility.
+    Usage: hfmt [-d|--print-diffs] [FILE]
+      Reformats Haskell source files by applying HLint, hindent, and
+      stylish-haskell.
 
     Available options:
       -h,--help                Show this help text
       -d,--print-diffs         If a file's formatting is different, print a diff.
       -s,--print-sources       If a file's formatting is different, print its
-                               source.
+                              source.
       -l,--print-paths         If a file's formatting is different, print its path.
       -w,--write-sources       If a file's formatting is different, overwrite it.
-      PATH                     Explicit paths to process.
+      FILE                     Explicit paths to process.
                                 - A single '-' will process standard input.
                                 - Files will be processed directly.
-                                - Directories will be recursively searched for
-                                  source files to process.
-                                - .cabal files will be parsed and all specified
-                                  source directories and files processed.
-                                - If no paths are given, the current directory will
-                                  be searched for .cabal files to process, if none
-                                  are found the current directory will be
-                                  recursively searched for source files to process.
+                                - Directories will be recursively searched for source files to process.
+                                - .cabal files will be parsed and all specified source directories and files processed.
+                                - If no paths are given, the current directory will be searched for .cabal files to process, if none are found the current directory will be recursively searched for source files to process.
+
+    Exit Codes:
+      0 = No error
+      1 = Encountered I/O or other operational error
+      2 = Failed to parse a source code file
+      3 = Source code was parsed but cannot be formatted properly
+      4 = Formatted code differs from existing source (--print-diffs only)
diff --git a/app/ExitCode.hs b/app/ExitCode.hs
--- a/app/ExitCode.hs
+++ b/app/ExitCode.hs
@@ -2,6 +2,7 @@
   ( exitCode
   , helpDoc
   , operationalFailureCode
+  , sourceParseFailureCode
   ) where
 
 import Types
@@ -15,13 +16,12 @@
 -- should exit with failure if any format differences were found. Otherwise
 -- assume we are being run in a context where non-zero exit indicates
 -- failure of the tool to operate properly.
-exitCode :: Action -> Bool -> ExitCode
-exitCode action formattedCodeDiffers =
-  if formattedCodeDiffers && failOnDifferences
-    then ExitFailure formattedCodeDiffersFailureCode
-    else ExitSuccess
-  where
-    failOnDifferences = action == PrintDiffs
+exitCode :: Action -> RunResult -> ExitCode
+exitCode _ NoDifferences = ExitSuccess
+exitCode PrintDiffs HadDifferences = ExitFailure formattedCodeDiffersFailureCode
+exitCode _ HadDifferences = ExitSuccess
+exitCode _ SourceParseFailure = ExitFailure sourceParseFailureCode
+exitCode _ OperationalFailure = ExitFailure operationalFailureCode
 
 helpDoc :: Doc
 helpDoc =
@@ -38,6 +38,10 @@
 -- | Encountered I/O or other operational error
 operationalFailureCode :: Int
 operationalFailureCode = 1
+
+-- | Failed to parse a source code file
+sourceParseFailureCode :: Int
+sourceParseFailureCode = 2
 
 -- | Formatted code differs from existing source code
 formattedCodeDiffersFailureCode :: Int
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -16,36 +16,44 @@
 import Options.Applicative.Extra          as OptApp
 import System.Directory
 import System.Exit
+import System.IO
 
 main :: IO ()
 main = do
   options <- execParser Options.parser
-  formattedCodeDiffers <- run options
-  exitWith $ exitCode (optAction options) formattedCodeDiffers
+  result <- run options
+  exitWith $ exitCode (optAction options) result
 
-run :: Options -> IO Bool
-run options =
+run :: Options -> IO RunResult
+run opt =
   runConduit $
-  sources .| mapMC readSource .| mapMC formatSource .| handleFormatErrors .|
-  mapMC action .|
-  summarize
+  sources opt .| mapMC readSource .| mapMC formatSource .| mapMC doAction .|
+  foldMapMC toRunResult
   where
-    paths = do
-      let explicitPaths = optPaths options
+    formatSource source = do
+      formatter <- defaultFormatter
+      return $ applyFormatter formatter source
+    doAction :: FormatResult -> IO FormatResult
+    doAction = bimapM return (Actions.act opt)
+    toRunResult :: FormatResult -> IO RunResult
+    toRunResult (Left err) = do
+      hPrint stderr (show err)
+      return SourceParseFailure
+    toRunResult (Right (Formatted _ source result)) =
+      if wasReformatted source result
+        then return HadDifferences
+        else return NoDifferences
+
+sources :: Options -> Source IO SourceFile
+sources opt = lift paths >>= mapM_ sourcesFromPath
+  where
+    explicitPaths = optPaths opt
+    paths =
       if null explicitPaths
         then do
           currentPath <- getCurrentDirectory
           return [currentPath]
         else return explicitPaths
-    sources :: Source IO SourceFile
-    sources = lift paths >>= mapM_ sourcesFromPath
-    formatSource source = do
-      formatter <- defaultFormatter
-      return $ applyFormatter formatter source
-    handleFormatErrors = concatMapMC handleFormatError
-    action = Actions.act options
-    summarize =
-      anyC' (\(Formatted _ source result) -> wasReformatted source result)
 
 sourcesFromPath :: FilePath -> Source IO SourceFile
 sourcesFromPath "-"  = yield StdinSource
@@ -63,19 +71,6 @@
     Left err       -> Left (FormatError file err)
     Right reformat -> Right (Formatted file contents reformat)
 
-handleFormatError :: FormatResult -> IO (Maybe Formatted)
-handleFormatError (Left err)        = printFormatError err >> return Nothing
-handleFormatError (Right formatted) = return $ Just formatted
-
-printFormatError :: FormatError -> IO ()
-printFormatError (FormatError input errorString) =
-  putStrLn ("Error reformatting " ++ show input ++ ": " ++ errorString)
-
--- | Check that at least one value in the stream returns True.
---
--- Does not shortcut, entire stream is always consumed
-anyC' :: Monad m => (a -> Bool) -> Consumer a m Bool
-anyC' f = do
-  result <- anyC f -- Check for at least one value, may shortcut
-  sinkNull -- consume any remaining input skipped by a shortcut
-  return result
+bimapM :: Monad m => (a -> m c) -> (b -> m d) -> Either a b -> m (Either c d)
+bimapM f _ (Left a)  = Left <$> f a
+bimapM _ g (Right b) = Right <$> g b
diff --git a/app/Types.hs b/app/Types.hs
--- a/app/Types.hs
+++ b/app/Types.hs
@@ -4,6 +4,7 @@
   , FormatError(..)
   , Formatted(..)
   , HaskellSource(..)
+  , RunResult(..)
   , SourceFile(..)
   , SourceFileWithContents(..)
   ) where
@@ -35,7 +36,25 @@
   FormatError SourceFile
               String
 
+instance Show FormatError where
+  show (FormatError input errorString) =
+    "Error reformatting " ++ show input ++ ": " ++ errorString
+
 data Formatted =
   Formatted SourceFile
             HaskellSource
             Reformatted
+
+data RunResult
+  = OperationalFailure
+  | SourceParseFailure
+  | HadDifferences
+  | NoDifferences
+
+instance Monoid RunResult where
+  mempty = NoDifferences
+  x `mappend` NoDifferences = x
+  NoDifferences `mappend` x = x
+  OperationalFailure `mappend` _ = OperationalFailure
+  SourceParseFailure `mappend` _ = SourceParseFailure
+  HadDifferences `mappend` _ = HadDifferences
diff --git a/hfmt.cabal b/hfmt.cabal
--- a/hfmt.cabal
+++ b/hfmt.cabal
@@ -1,5 +1,5 @@
 name:                hfmt
-version:             0.2.0
+version:             0.2.1
 synopsis:            Haskell source code formatter
 description:         Inspired by gofmt. Built using hlint, hindent, and stylish-haskell.
 license:             MIT
@@ -30,6 +30,7 @@
   Build-Depends:     base >= 4.8 && < 5
                      , bytestring
                      , Cabal
+                     , conduit
                      , conduit-combinators
                      , Diff
                      , directory
@@ -56,6 +57,7 @@
                    , Types
   GHC-Options:       -Wall
   Build-Depends:     base >= 4.8 && < 5
+                   , conduit
                    , conduit-combinators
                    , directory
                    , hfmt
diff --git a/src/Language/Haskell/Format/HIndent.hs b/src/Language/Haskell/Format/HIndent.hs
--- a/src/Language/Haskell/Format/HIndent.hs
+++ b/src/Language/Haskell/Format/HIndent.hs
@@ -29,7 +29,7 @@
 autoSettings :: IO Settings
 autoSettings = do
   config <- getConfig
-  return (Settings config Nothing)
+  return $ Settings config $ Just defaultExtensions
 
 -- | Read config from a config file, or return 'defaultConfig'.
 getConfig :: IO Config
