diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,9 @@
 Changelog for ghcid (* = breaking change)
 
+0.8.9, released 2023-07-02
+    #375, fix crash when modules are renamed or deleted in GHC 9.6
+    #378, write output of the linter into output files
+    Support newer Win32 libraries
 0.8.8, released 2022-09-17
     #365, support fsnotify 0.4
     #348, improve the no files loaded message
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Neil Mitchell 2014-2022.
+Copyright Neil Mitchell 2014-2023.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -77,7 +77,13 @@
 Yes, that's a [bug in GHCi](https://ghc.haskell.org/trac/ghc/ticket/9648). If you see GHCi getting confused just kill `ghcid` and start it again.
 
 #### I want to run arbitrary commands when arbitrary files change.
-This project reloads `ghci` when files loaded by `ghci` change. If you want a more general mechanism something like [Steel Overseer](https://github.com/schell/steeloverseer) or [Watchman](https://facebook.github.io/watchman/) will probably work better.
+This project reloads `ghci` when files loaded by `ghci` change. If you want a more general mechanism, consider:
+
+* [Steel Overseer](https://github.com/schell/steeloverseer) ([Hackage](https://hackage.haskell.org/package/steeloverseer))
+* [Watchman](https://facebook.github.io/watchman/)
+* [`feedback`](https://github.com/NorfairKing/feedback) ([Hackage](https://hackage.haskell.org/package/feedback))
+* [Watchexec](https://github.com/watchexec/watchexec)
+* [`entr`](https://github.com/eradman/entr)
 
 #### I want syntax highlighting in the error messages.
 One option is to use Neovim or Emacs and run the terminal in a buffer whose file type is set to Haskell. Another option is to pipe `ghcid` through [source-highlight](https://www.gnu.org/software/src-highlite/) (`ghcid | source-highlight -s haskell -f esc`).
diff --git a/ghcid.cabal b/ghcid.cabal
--- a/ghcid.cabal
+++ b/ghcid.cabal
@@ -1,19 +1,19 @@
 cabal-version:      1.18
 build-type:         Simple
 name:               ghcid
-version:            0.8.8
+version:            0.8.9
 license:            BSD3
 license-file:       LICENSE
 category:           Development
 author:             Neil Mitchell <ndmitchell@gmail.com>, jpmoresmau
 maintainer:         Neil Mitchell <ndmitchell@gmail.com>
-copyright:          Neil Mitchell 2014-2022
+copyright:          Neil Mitchell 2014-2023
 synopsis:           GHCi based bare bones IDE
 description:
     Either \"GHCi as a daemon\" or \"GHC + a bit of an IDE\". A very simple Haskell development tool which shows you the errors in your project and updates them whenever you save. Run @ghcid --topmost --command=ghci@, where @--topmost@ makes the window on top of all others (Windows only) and @--command@ is the command to start GHCi on your project (defaults to @ghci@ if you have a @.ghci@ file, or else to @cabal repl@).
 homepage:           https://github.com/ndmitchell/ghcid#readme
 bug-reports:        https://github.com/ndmitchell/ghcid/issues
-tested-with:        GHC==9.0, GHC==8.10, GHC==8.8, GHC==8.6
+tested-with:        GHC==9.6, GHC==9.4, GHC==9.2, GHC==9.0, GHC==8.10, GHC==8.8
 extra-doc-files:
     CHANGES.txt
     README.md
@@ -62,7 +62,7 @@
         ansi-terminal,
         terminal-size >= 0.3
     if os(windows)
-        build-depends: Win32
+        build-depends: Win32 >= 2.13.2.1
     else
         build-depends: unix
     other-modules:
@@ -89,7 +89,7 @@
         directory >= 1.2,
         process,
         containers,
-        fsnotify,
+        fsnotify >= 0.4,
         extra >= 1.6.6,
         ansi-terminal,
         terminal-size >= 0.3,
diff --git a/src/Ghcid.hs b/src/Ghcid.hs
--- a/src/Ghcid.hs
+++ b/src/Ghcid.hs
@@ -402,7 +402,10 @@
             whenJust lint $ \lintcmd ->
                 unless hasErrors $ do
                     (exitcode, stdout, stderr) <- readCreateProcessWithExitCode (shell . unwords $ lintcmd : map escape touched) ""
-                    unless (exitcode == ExitSuccess) $ outStrLn (stdout ++ stderr)
+                    unless (exitcode == ExitSuccess) $ do
+                        let output = stdout ++ stderr
+                        outStrLn output
+                        forM_ outputfile $ flip writeFile output
 
             reason <- nextWait $ map (,Restart) restart
                               ++ map (,Reload) reload
diff --git a/src/Language/Haskell/Ghcid/Parser.hs b/src/Language/Haskell/Ghcid/Parser.hs
--- a/src/Language/Haskell/Ghcid/Parser.hs
+++ b/src/Language/Haskell/Ghcid/Parser.hs
@@ -64,7 +64,7 @@
 
         -- <no location info>: error:
         f (x:xs)
-            | unescapeE x == "<no location info>: error:"
+            | "<no location info>: error:" `isPrefixOfE` x
             , (xs,rest) <- span leadingWhitespaceE xs
             = Message Error "<unknown>" (0,0) (0,0) (map fromEsc $ x:xs) : f rest
 
diff --git a/src/Language/Haskell/Ghcid/Terminal.hs b/src/Language/Haskell/Ghcid/Terminal.hs
--- a/src/Language/Haskell/Ghcid/Terminal.hs
+++ b/src/Language/Haskell/Ghcid/Terminal.hs
@@ -18,11 +18,7 @@
 import System.Win32.Types
 
 
-wM_SETICON = 0x0080 :: WindowMessage
 wM_GETICON = 0x007F :: WindowMessage
-
-iCON_BIG = 1
-iCON_SMALL = 0
 
 #ifdef x86_64_HOST_ARCH
 #define CALLCONV ccall
diff --git a/src/Session.hs b/src/Session.hs
--- a/src/Session.hs
+++ b/src/Session.hs
@@ -81,11 +81,15 @@
     -- See: https://github.com/ndmitchell/ghcid/issues/254
     showCursor
 
-loadedModules :: [Load] -> [FilePath]
-loadedModules = nubOrd . map loadFile . filter (not . isLoadConfig)
+loadedModules :: FilePath -> [Load] -> [FilePath]
+loadedModules dir = nubOrd . map (loadFile . qualify dir) . filter predicate
+    where
+      predicate Message{loadFile = loadFile} = loadFile /= "<unknown>"
+      predicate Loading{loadFile = loadFile} = loadFile /= "<unknown>"
+      predicate _ = False
 
-qualify :: FilePath -> [Load] -> [Load]
-qualify dir xs = [x{loadFile = dir </> loadFile x} | x <- xs]
+qualify :: FilePath -> Load -> Load
+qualify dir message = message{loadFile = dir </> loadFile message}
 
 -- | Spawn a new Ghci process at a given command line. Returns the load messages, plus
 --   the list of files that were observed (both those loaded and those that failed to load).
@@ -112,9 +116,9 @@
     -- deal with current directory
     (dir, _) <- showPaths v
     writeIORef curdir dir
-    messages <- pure $ qualify dir messages
+    messages <- pure $ map (qualify dir) messages
 
-    let loaded = loadedModules messages
+    let loaded = loadedModules dir messages
     evals <- performEvals v allowEval loaded
 
     -- install a handler
@@ -208,9 +212,9 @@
         -- actually reload
         Just ghci <- readIORef ghci
         dir <- readIORef curdir
-        messages <- mapMaybe tidyMessage . qualify dir <$> reload ghci
+        messages <- mapMaybe tidyMessage <$> reload ghci
         loaded <- map ((dir </>) . snd) <$> showModules ghci
-        let reloaded = loadedModules messages
+        let reloaded = loadedModules dir messages
         warn <- readIORef warnings
         evals <- performEvals ghci allowEval reloaded
 
diff --git a/src/Test/Parser.hs b/src/Test/Parser.hs
--- a/src/Test/Parser.hs
+++ b/src/Test/Parser.hs
@@ -105,7 +105,7 @@
 testMissingFile = testCase "Starting ghci with a non-existent filename" $ parseLoad
     ["<no location info>: error: can't find file: bob.hs"
     ] @?=
-    []
+    [Message Error "<unknown>" (0,0) (0,0) ["<no location info>: error: can't find file: bob.hs"]]
 
 testParseLoadCyclesSelf = testCase "Module cycle with itself" $ parseLoad
     ["Module imports form a cycle:"
