diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,8 @@
 Changelog for GHCiD
 
+0.3.3
+    #21, if the root file is missing, report an error
+    #20, avoid an O(n^2) nub
 0.3.2
     #18, reformat excessively long lines, add a --width flag
 0.3.1
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# ghcid [![Hackage version](https://img.shields.io/hackage/v/ghcid.svg?style=flat)](http://hackage.haskell.org/package/ghcid) [![Build Status](http://img.shields.io/travis/ndmitchell/ghcid.svg?style=flat)](https://travis-ci.org/ndmitchell/ghcid)
+# ghcid [![Hackage version](https://img.shields.io/hackage/v/ghcid.svg?style=flat)](https://hackage.haskell.org/package/ghcid) [![Build Status](https://img.shields.io/travis/ndmitchell/ghcid.svg?style=flat)](https://travis-ci.org/ndmitchell/ghcid)
 
 Either "GHCi as a daemon" or "GHC + a bit of an IDE". To a first approximation, it opens `ghci` and runs `:reload` whenever your source code changes, formatting the output to fit a fixed height console. Unlike other Haskell development tools, `ghcid` is intended to be _incredibly simple_. In particular, it doesn't integrate with any editors, doesn't depend on GHC the library and doesn't start web servers.
 
diff --git a/ghcid.cabal b/ghcid.cabal
--- a/ghcid.cabal
+++ b/ghcid.cabal
@@ -1,7 +1,7 @@
 cabal-version:      >= 1.10
 build-type:         Simple
 name:               ghcid
-version:            0.3.2
+version:            0.3.3
 license:            BSD3
 license-file:       LICENSE
 category:           Development
@@ -30,6 +30,7 @@
         filepath,
         time,
         directory,
+        containers,
         extra >= 0.4,
         process >= 1.1,
         cmdargs >= 0.10
@@ -52,12 +53,12 @@
       base == 4.*,
       filepath,
       time,
+      containers,
       directory,
       extra >= 0.4,
       process >= 1.1,
-      cmdargs >= 0.10
-  if !os(windows)
-    build-depends: terminal-size >= 0.2.1
+      cmdargs >= 0.10,
+      terminal-size >= 0.3
   other-modules:   
                    Language.Haskell.Ghcid.Types,
                    Language.Haskell.Ghcid.Parser,
@@ -74,6 +75,7 @@
     filepath,
     time,
     directory,
+    containers,
     process,
     extra >= 0.4,
     cmdargs,
diff --git a/src/Ghcid.hs b/src/Ghcid.hs
--- a/src/Ghcid.hs
+++ b/src/Ghcid.hs
@@ -9,7 +9,9 @@
 import Data.List.Extra
 import Data.Maybe
 import Data.Time.Clock
+import Data.Tuple.Extra
 import Data.Version
+import qualified System.Console.Terminal.Size as Term
 import System.Console.CmdArgs
 import System.Directory
 import System.IO
@@ -48,7 +50,7 @@
     height <- return $ case (width, height) of
         (Just w, Just h) -> return (w,h)
         _ -> do
-            term <- terminalSize
+            term <- fmap (fmap $ Term.width &&& Term.height) Term.size
             let f user def sel = fromMaybe (maybe def sel term) user
             -- if we write to the end of the window then it wraps automatically
             -- so putStrLn width 'x' uses up two lines
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
@@ -9,20 +9,22 @@
 
 import System.FilePath
 import Data.Char
-import Data.List
+import Data.List.Extra
 
 import Language.Haskell.Ghcid.Types
+import Language.Haskell.Ghcid.Util
 
+
 -- | Parse messages from show modules command
 parseShowModules :: [String] -> [(String, FilePath)]
 parseShowModules xs =
-    [ (takeWhile (not . isSpace) $ dropWhile isSpace a, takeWhile (/= ',') b)
+    [ (takeWhile (not . isSpace) $ trimStart a, takeWhile (/= ',') b)
     | x <- xs, (a,'(':' ':b) <- [break (== '(') x]]
 
 -- | Parse messages given on reload
 -- nub, because cabal repl sometimes does two reloads at the start
 parseLoad :: [String] -> [Load]
-parseLoad  = nub . parseLoad'            
+parseLoad  = ordNub . parseLoad'            
 
 -- | Parse messages given on reload
 parseLoad' :: [String] -> [Load]
@@ -36,8 +38,11 @@
     , (pos,rest2) <- span (\c -> c == ':' || isDigit c) rest
     , [p1,p2] <- map read $ words $ map (\c -> if c == ':' then ' ' else c) pos 
     , (msg,las) <- span (isPrefixOf " ") xs
-    , rest3 <- dropWhile isSpace rest2
+    , rest3 <- trimStart rest2
     , sev <- if "Warning:" `isPrefixOf` rest3 then Warning else Error
     = Message sev file (p1,p2) (x:msg) : parseLoad las
+parseLoad' (x:xs)
+    | Just file <- stripPrefix "<no location info>: can't find file: " x
+    = Message Error file (0,0) [file ++ ": Can't find file"] : parseLoad xs
 parseLoad' (_:xs) = parseLoad xs
 parseLoad' [] = []
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
@@ -1,64 +1,24 @@
 {-# LANGUAGE CPP #-}
 module Language.Haskell.Ghcid.Terminal(
-    terminalSize, terminalTopmost
+    terminalTopmost
     ) where
 
-#if !defined(mingw32_HOST_OS)
-import qualified System.Console.Terminal.Size as Terminal
-import System.IO (stdout)
-import Data.Tuple.Extra
-#endif
-
 #if defined(mingw32_HOST_OS)
-import Control.Monad
 import Data.Word
 import Data.Bits
 import Foreign.Ptr
-import Foreign.Storable
-import Foreign.Marshal.Alloc
 
-type HANDLE = Ptr ()
 type HWND = Ptr ()
 
-data CONSOLE_SCREEN_BUFFER_INFO
-sizeCONSOLE_SCREEN_BUFFER_INFO = 22
-posCONSOLE_SCREEN_BUFFER_INFO_srWindow = 10 -- 4 x Word16 Left,Top,Right,Bottom
-
 c_SWP_NOSIZE = 1 :: Word32
 c_SWP_NOMOVE = 2 :: Word32
 c_HWND_TOPMOST = -1 :: Int
-c_STD_OUTPUT_HANDLE = -11 :: Word32
 
 foreign import stdcall unsafe "windows.h GetConsoleWindow"
     c_GetConsoleWindow :: IO HWND
 
 foreign import stdcall unsafe "windows.h SetWindowPos"
     c_SetWindowPos :: HWND -> Int -> Int -> Int -> Int -> Int -> Word32 -> IO Bool
-
-foreign import stdcall unsafe "windows.h GetConsoleScreenBufferInfo"
-    c_GetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO Bool
-
-foreign import stdcall unsafe "windows.h GetStdHandle"
-    c_GetStdHandle :: Word32 -> IO HANDLE
-#endif
-
-
--- | Figure out the size of the current terminal, @(width,height)@, or return 'Nothing'.
-terminalSize :: IO (Maybe (Int, Int))
-#if defined(mingw32_HOST_OS)
-terminalSize = do
-    hdl <- c_GetStdHandle c_STD_OUTPUT_HANDLE
-    allocaBytes sizeCONSOLE_SCREEN_BUFFER_INFO $ \p -> do
-        b <- c_GetConsoleScreenBufferInfo hdl p
-        if not b then return Nothing else do
-            [left,top,right,bottom] <- forM [0..3] $ \i -> do
-                v <- peekByteOff p ((i*2) + posCONSOLE_SCREEN_BUFFER_INFO_srWindow)
-                return $ fromIntegral (v :: Word16)
-            return $ Just (1+right-left, 1+bottom-top)
-#else
-terminalSize = do
-    s <- Terminal.hSize stdout
-    return $ fmap (Terminal.width &&& Terminal.height) s
 #endif
 
 
diff --git a/src/Language/Haskell/Ghcid/Types.hs b/src/Language/Haskell/Ghcid/Types.hs
--- a/src/Language/Haskell/Ghcid/Types.hs
+++ b/src/Language/Haskell/Ghcid/Types.hs
@@ -29,7 +29,7 @@
         ,loadFilePos :: (Int,Int)
         ,loadMessage :: [String]
         }
-      deriving (Show,Eq)
+      deriving (Show, Eq, Ord)
       
 -- | Is a Load a message with severity?
 isMessage :: Load -> Bool
diff --git a/src/Language/Haskell/Ghcid/Util.hs b/src/Language/Haskell/Ghcid/Util.hs
--- a/src/Language/Haskell/Ghcid/Util.hs
+++ b/src/Language/Haskell/Ghcid/Util.hs
@@ -6,12 +6,14 @@
   , outStrLn
   , outStr
   , allGoodMessage
+  , ordNub
   ) where
 
 import Control.Concurrent.Extra
 import System.IO.Unsafe
 import Data.List.Extra
 import Data.Char
+import qualified Data.Set as Set
 
 
 -- | Drop a prefix from a list, no matter how many times that prefix is present
@@ -43,3 +45,14 @@
     if null b then (a, []) else
         let (a1,a2) = breakEnd isSpace a in
         if length a2 <= gap then (a1, a2 ++ b) else (a, dropWhile isSpace b)
+
+
+-- ordNub function from <https://github.com/nh2/haskell-ordnub>
+ordNub :: (Ord a) => [a] -> [a]
+ordNub l = go Set.empty l
+  where
+    go _ [] = []
+    go s (x:xs) =
+      if x `Set.member` s
+        then go s xs
+        else x : go (Set.insert x s) xs
