diff --git a/src/ghci/mod.rs b/src/ghci/mod.rs
index d5b1be0a..6f61bf66 100644
--- a/src/ghci/mod.rs
+++ b/src/ghci/mod.rs
@@ -80,6 +80,8 @@ use crate::shutdown::ShutdownHandle;
 use crate::CommandExt;
 use crate::StringCase;
 
+use self::parse::TargetKind;
+
 /// The `ghci` prompt we use. Should be unique enough, but maybe we can make it better with Unicode
 /// private-use-area codepoints or something in the future.
 pub const PROMPT: &str = "###~GHCIWATCH-PROMPT~###";
@@ -666,7 +668,8 @@ impl Ghci {
             .add_module(&mut self.stdout, path.relative(), log)
             .await?;
 
-        self.targets.insert_source_path(path.clone());
+        self.targets
+            .insert_source_path(path.clone(), TargetKind::Path);
 
         self.refresh_eval_commands_for_paths(std::iter::once(path))
             .await?;
@@ -685,11 +688,15 @@ impl Ghci {
         path: &NormalPath,
         log: &mut CompilationLog,
     ) -> miette::Result<()> {
+        let (import_name, _target_kind) =
+            self.targets.module_import_name(&self.search_paths, path)?;
+
         self.stdin
-            .interpret_module(&mut self.stdout, path.relative(), log)
+            .interpret_module(&mut self.stdout, &import_name, log)
             .await?;
 
-        self.targets.insert_source_path(path.clone());
+        self.targets
+            .insert_source_path(path.clone(), TargetKind::Path);
 
         self.refresh_eval_commands_for_paths(std::iter::once(path))
             .await?;
diff --git a/src/ghci/parse/mod.rs b/src/ghci/parse/mod.rs
index 178a1516..13c89e57 100644
--- a/src/ghci/parse/mod.rs
+++ b/src/ghci/parse/mod.rs
@@ -8,6 +8,7 @@ mod module_and_files;
 mod module_set;
 mod show_paths;
 mod show_targets;
+mod target_kind;
 
 use haskell_grammar::module_name;
 use lines::rest_of_line;
@@ -26,3 +27,4 @@ pub use module_set::ModuleSet;
 pub use show_paths::parse_show_paths;
 pub use show_paths::ShowPaths;
 pub use show_targets::parse_show_targets;
+pub use target_kind::TargetKind;
diff --git a/src/ghci/parse/module_set.rs b/src/ghci/parse/module_set.rs
index b353294b..94c36793 100644
--- a/src/ghci/parse/module_set.rs
+++ b/src/ghci/parse/module_set.rs
@@ -1,37 +1,42 @@
 use std::borrow::Borrow;
 use std::cmp::Eq;
-use std::collections::hash_set::Iter;
-use std::collections::HashSet;
+use std::collections::hash_map::Keys;
+use std::collections::HashMap;
 use std::hash::Hash;
 use std::path::Path;
 
 use crate::normal_path::NormalPath;
 
+use super::ShowPaths;
+use super::TargetKind;
+
 /// A collection of source paths, retaining information about loaded modules in a `ghci`
 /// session.
 #[derive(Debug, Clone, Default, PartialEq, Eq)]
 pub struct ModuleSet {
-    set: HashSet<NormalPath>,
+    modules: HashMap<NormalPath, TargetKind>,
 }
 
 impl ModuleSet {
     /// Construct a `ModuleSet` from an iterator of module source paths.
     pub fn from_paths(
-        paths: impl IntoIterator<Item = impl AsRef<Path>>,
+        paths: impl IntoIterator<Item = (impl AsRef<Path>, TargetKind)>,
         current_dir: impl AsRef<Path>,
     ) -> miette::Result<Self> {
         let current_dir = current_dir.as_ref();
         Ok(Self {
-            set: paths
+            modules: paths
                 .into_iter()
-                .map(|path| NormalPath::new(path.as_ref(), current_dir))
+                .map(|(path, kind)| {
+                    NormalPath::new(path.as_ref(), current_dir).map(|path| (path, kind))
+                })
                 .collect::<Result<_, _>>()?,
         })
     }
 
     /// Get the number of modules in this set.
     pub fn len(&self) -> usize {
-        self.set.len()
+        self.modules.len()
     }
 
     /// Determine if a module with the given source path is contained in this module set.
@@ -40,18 +45,47 @@ impl ModuleSet {
         NormalPath: Borrow<P>,
         P: Hash + Eq + ?Sized,
     {
-        self.set.contains(path)
+        self.modules.contains_key(path)
     }
 
     /// Add a source path to this module set.
     ///
     /// Returns whether the value was newly inserted.
-    pub fn insert_source_path(&mut self, path: NormalPath) -> bool {
-        self.set.insert(path)
+    pub fn insert_source_path(&mut self, path: NormalPath, kind: TargetKind) -> bool {
+        self.modules.insert(path, kind).is_some()
+    }
+
+    /// Get the name used to refer to the given module path when importing it.
+    ///
+    /// If the module isn't imported, a path will be returned.
+    ///
+    /// Otherwise, the form used to import the module originally will be used. Generally this is a
+    /// path if `ghciwatch` imported the module, and a module name if `ghci` imported the module on
+    /// startup.
+    ///
+    /// See: <https://gitlab.haskell.org/ghc/ghc/-/issues/13254#note_525037>
+    pub fn module_import_name(
+        &self,
+        show_paths: &ShowPaths,
+        path: &NormalPath,
+    ) -> miette::Result<(String, TargetKind)> {
+        match self.modules.get(path) {
+            Some(kind) => match kind {
+                TargetKind::Path => Ok((path.relative().to_string(), *kind)),
+                TargetKind::Module => {
+                    let module = show_paths.path_to_module(path)?;
+                    Ok((module, *kind))
+                }
+            },
+            None => {
+                let path = show_paths.make_relative(path)?;
+                Ok((path.into_relative().into_string(), TargetKind::Path))
+            }
+        }
     }
 
     /// Iterate over the source paths in this module set.
-    pub fn iter(&self) -> Iter<'_, NormalPath> {
-        self.set.iter()
+    pub fn iter(&self) -> Keys<'_, NormalPath, TargetKind> {
+        self.modules.keys()
     }
 }
diff --git a/src/ghci/parse/show_paths.rs b/src/ghci/parse/show_paths.rs
index 83cb10c3..49a5abd6 100644
--- a/src/ghci/parse/show_paths.rs
+++ b/src/ghci/parse/show_paths.rs
@@ -19,6 +19,7 @@ use crate::haskell_source_file::HASKELL_SOURCE_EXTENSIONS;
 use crate::normal_path::NormalPath;
 
 use super::lines::until_newline;
+use super::TargetKind;
 
 /// Parsed `:show paths` output.
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -36,13 +37,13 @@ impl ShowPaths {
     }
 
     /// Convert a target (from `:show targets` output) to a module source path.
-    pub fn target_to_path(&self, target: &str) -> miette::Result<Utf8PathBuf> {
+    pub fn target_to_path(&self, target: &str) -> miette::Result<(Utf8PathBuf, TargetKind)> {
         let target_path = Utf8Path::new(target);
         if is_haskell_source_file(target_path) {
             // The target is already a path.
             if let Some(path) = self.target_path_to_path(target_path) {
                 tracing::trace!(%path, %target, "Target is path");
-                return Ok(path);
+                return Ok((path, TargetKind::Path));
             }
         } else {
             // Else, split by `.` to get path components.
@@ -54,7 +55,7 @@ impl ShowPaths {
 
                 if let Some(path) = self.target_path_to_path(&path) {
                     tracing::trace!(%path, %target, "Found path for target");
-                    return Ok(path);
+                    return Ok((path, TargetKind::Module));
                 }
             }
         }
diff --git a/src/ghci/parse/show_targets.rs b/src/ghci/parse/show_targets.rs
index 75ac8e32..8dcc6f2d 100644
--- a/src/ghci/parse/show_targets.rs
+++ b/src/ghci/parse/show_targets.rs
@@ -5,12 +5,13 @@ use winnow::Parser;
 
 use super::lines::until_newline;
 use super::show_paths::ShowPaths;
+use super::TargetKind;
 
 /// Parse `:show targets` output into a set of module source paths.
 pub fn parse_show_targets(
     search_paths: &ShowPaths,
     input: &str,
-) -> miette::Result<Vec<Utf8PathBuf>> {
+) -> miette::Result<Vec<(Utf8PathBuf, TargetKind)>> {
     let targets: Vec<_> = repeat(0.., until_newline)
         .parse(input)
         .map_err(|err| miette!("{err}"))?;
@@ -43,6 +44,7 @@ mod tests {
                 indoc!(
                     "
                     src/MyLib.hs
+                    MyLib.hs
                     TestMain
                     MyLib
                     MyModule
@@ -51,10 +53,26 @@ mod tests {
             )
             .unwrap(),
             vec![
-                Utf8PathBuf::from("tests/data/simple/src/MyLib.hs"),
-                Utf8PathBuf::from("tests/data/simple/test/TestMain.hs"),
-                Utf8PathBuf::from("tests/data/simple/src/MyLib.hs"),
-                Utf8PathBuf::from("tests/data/simple/src/MyModule.hs"),
+                (
+                    Utf8PathBuf::from("tests/data/simple/src/MyLib.hs"),
+                    TargetKind::Path
+                ),
+                (
+                    Utf8PathBuf::from("tests/data/simple/src/MyLib.hs"),
+                    TargetKind::Path
+                ),
+                (
+                    Utf8PathBuf::from("tests/data/simple/test/TestMain.hs"),
+                    TargetKind::Module
+                ),
+                (
+                    Utf8PathBuf::from("tests/data/simple/src/MyLib.hs"),
+                    TargetKind::Module
+                ),
+                (
+                    Utf8PathBuf::from("tests/data/simple/src/MyModule.hs"),
+                    TargetKind::Module
+                ),
             ]
         );
     }
diff --git a/src/ghci/parse/target_kind.rs b/src/ghci/parse/target_kind.rs
new file mode 100644
index 00000000..118edd9e
--- /dev/null
+++ b/src/ghci/parse/target_kind.rs
@@ -0,0 +1,12 @@
+/// Entries in `:show targets` can be one of two types: module paths or module names (with `.` in
+/// place of path separators). Due to a `ghci` bug, the module can only be referred to as whichever
+/// form it was originally added as (see below), so we use this to track how we refer to modules.
+///
+/// See: <https://gitlab.haskell.org/ghc/ghc/-/issues/13254#note_525037>
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum TargetKind {
+    /// A target named by its source path.
+    Path,
+    /// A target named by its module name.
+    Module,
+}
diff --git a/src/ghci/stdin.rs b/src/ghci/stdin.rs
index 099d20f2..dad957a2 100644
--- a/src/ghci/stdin.rs
+++ b/src/ghci/stdin.rs
@@ -125,12 +125,12 @@ impl GhciStdin {
     pub async fn interpret_module(
         &mut self,
         stdout: &mut GhciStdout,
-        path: &Utf8Path,
+        module: &str,
         log: &mut CompilationLog,
     ) -> miette::Result<()> {
         // `:add *` forces the module to be interpreted, even if it was already loaded from
         // bytecode. This is necessary to access the module's top-level binds for the eval feature.
-        self.write_line(stdout, &format!(":add *{path}\n"), log)
+        self.write_line(stdout, &format!(":add *{module}\n"), log)
             .await
     }
 
diff --git a/src/normal_path.rs b/src/normal_path.rs
index 2b63c079..9cfad3f4 100644
--- a/src/normal_path.rs
+++ b/src/normal_path.rs
@@ -61,6 +61,18 @@ impl NormalPath {
     pub fn relative(&self) -> &Utf8Path {
         self.relative.as_deref().unwrap_or_else(|| self.absolute())
     }
+
+    /// Get the absolute path, consuming this value.
+    pub fn into_absolute(self) -> Utf8PathBuf {
+        self.normal
+    }
+
+    /// Get the relative path, consuming this value.
+    ///
+    /// If no relative path is present, the absolute (normalized) path is used instead.
+    pub fn into_relative(self) -> Utf8PathBuf {
+        self.relative.unwrap_or(self.normal)
+    }
 }
 
 // Hash, Eq, and Ord delegate to the normalized path.
diff --git a/tests/eval.rs b/tests/eval.rs
index e88baf28..8f9bfeaa 100644
--- a/tests/eval.rs
+++ b/tests/eval.rs
@@ -30,9 +30,13 @@ async fn can_eval_commands() {
         .await
         .expect("ghciwatch didn't start in time");
 
-    let eval_message = BaseMatcher::message(r"MyModule.hs:\d+:\d+: example \+\+ example");
+    let defined_in_multiple_files =
+        BaseMatcher::message("Read stderr line").with_field("line", "defined in multiple files");
+
+    let eval_message = BaseMatcher::message(r"MyModule.hs:\d+:\d+: example \+\+ example")
+        .but_not(defined_in_multiple_files.clone());
     session
-        .assert_logged_or_wait(&eval_message)
+        .assert_logged_or_wait(eval_message.clone())
         .await
         .expect("ghciwatch evals commands");
     session
@@ -41,6 +45,14 @@ async fn can_eval_commands() {
         )
         .await
         .expect("ghciwatch evals commands");
+    session
+        .assert_logged_or_wait(
+            BaseMatcher::span_close()
+                .in_leaf_spans(["run_ghci", "initialize"])
+                .but_not(defined_in_multiple_files.clone()),
+        )
+        .await
+        .expect("ghciwatch finishes initializing");
 
     // Erase the command.
     session.fs().replace(module_path, cmd, "").await.unwrap();