diff --git a/src/main.rs b/src/main.rs
index 6796921..28a426b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -36,6 +36,10 @@ use self::{
 struct Args {
     #[command(subcommand)]
     command: Option<Subcommands>,
+    /// Manually run the current exercise using `r` or `run` in the watch mode.
+    /// Only use this if Rustlings fails to detect exercise file changes.
+    #[arg(long)]
+    manual_run: bool,
 }
 
 #[derive(Subcommand)]
@@ -101,17 +105,23 @@ fn main() -> Result<()> {
 
     match args.command {
         None => {
-            // For the the notify event handler thread.
-            // Leaking is not a problem because the slice lives until the end of the program.
-            let exercise_paths = app_state
-                .exercises()
-                .iter()
-                .map(|exercise| exercise.path)
-                .collect::<Vec<_>>()
-                .leak();
+            let notify_exercise_paths: Option<&'static [&'static str]> = if args.manual_run {
+                None
+            } else {
+                // For the the notify event handler thread.
+                // Leaking is not a problem because the slice lives until the end of the program.
+                Some(
+                    app_state
+                        .exercises()
+                        .iter()
+                        .map(|exercise| exercise.path)
+                        .collect::<Vec<_>>()
+                        .leak(),
+                )
+            };
 
             loop {
-                match watch(&mut app_state, exercise_paths)? {
+                match watch(&mut app_state, notify_exercise_paths)? {
                     WatchExit::Shutdown => break,
                     // It is much easier to exit the watch mode, launch the list mode and then restart
                     // the watch mode instead of trying to pause the watch threads and correct the
diff --git a/src/watch.rs b/src/watch.rs
index bab64ae..d20e552 100644
--- a/src/watch.rs
+++ b/src/watch.rs
@@ -42,25 +42,38 @@ pub enum WatchExit {
 
 pub fn watch(
     app_state: &mut AppState,
-    exercise_paths: &'static [&'static str],
+    notify_exercise_paths: Option<&'static [&'static str]>,
 ) -> Result<WatchExit> {
     let (tx, rx) = channel();
-    let mut debouncer = new_debouncer(
-        Duration::from_secs(1),
-        DebounceEventHandler {
-            tx: tx.clone(),
-            exercise_paths,
-        },
-    )?;
-    debouncer
-        .watcher()
-        .watch(Path::new("exercises"), RecursiveMode::Recursive)?;
 
-    let mut watch_state = WatchState::new(app_state);
+    let mut manual_run = false;
+    // Prevent dropping the guard until the end of the function.
+    // Otherwise, the file watcher exits.
+    let _debouncer_guard = if let Some(exercise_paths) = notify_exercise_paths {
+        let mut debouncer = new_debouncer(
+            Duration::from_secs(1),
+            DebounceEventHandler {
+                tx: tx.clone(),
+                exercise_paths,
+            },
+        )
+        .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?;
+        debouncer
+            .watcher()
+            .watch(Path::new("exercises"), RecursiveMode::Recursive)
+            .inspect_err(|_| eprintln!("{NOTIFY_ERR}"))?;
+
+        Some(debouncer)
+    } else {
+        manual_run = true;
+        None
+    };
+
+    let mut watch_state = WatchState::new(app_state, manual_run);
 
     watch_state.run_current_exercise()?;
 
-    thread::spawn(move || terminal_event_handler(tx));
+    thread::spawn(move || terminal_event_handler(tx, manual_run));
 
     while let Ok(event) = rx.recv() {
         match event {
@@ -78,6 +91,7 @@ pub fn watch(
                 watch_state.into_writer().write_all(QUIT_MSG)?;
                 break;
             }
+            WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise()?,
             WatchEvent::Input(InputEvent::Unrecognized(cmd)) => {
                 watch_state.handle_invalid_cmd(&cmd)?;
             }
@@ -88,7 +102,8 @@ pub fn watch(
                 watch_state.render()?;
             }
             WatchEvent::NotifyErr(e) => {
-                return Err(Error::from(e).context("Exercise file watcher failed"));
+                watch_state.into_writer().write_all(NOTIFY_ERR.as_bytes())?;
+                return Err(Error::from(e));
             }
             WatchEvent::TerminalEventErr(e) => {
                 return Err(Error::from(e).context("Terminal event listener failed"));
@@ -103,3 +118,11 @@ const QUIT_MSG: &[u8] = b"
 We hope you're enjoying learning Rust!
 If you want to continue working on the exercises at a later point, you can simply run `rustlings` again.
 ";
+
+const NOTIFY_ERR: &str = "
+The automatic detection of exercise file changes failed :(
+Please try running `rustlings` again.
+
+If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher.
+You need to manually trigger running the current exercise using `r` or `run` then.
+";
diff --git a/src/watch/state.rs b/src/watch/state.rs
index 1a79573..c0f6c53 100644
--- a/src/watch/state.rs
+++ b/src/watch/state.rs
@@ -18,10 +18,11 @@ pub struct WatchState<'a> {
     stderr: Option<Vec<u8>>,
     show_hint: bool,
     show_done: bool,
+    manual_run: bool,
 }
 
 impl<'a> WatchState<'a> {
-    pub fn new(app_state: &'a mut AppState) -> Self {
+    pub fn new(app_state: &'a mut AppState, manual_run: bool) -> Self {
         let writer = io::stdout().lock();
 
         Self {
@@ -31,6 +32,7 @@ impl<'a> WatchState<'a> {
             stderr: None,
             show_hint: false,
             show_done: false,
+            manual_run,
         }
     }
 
@@ -78,6 +80,10 @@ impl<'a> WatchState<'a> {
     fn show_prompt(&mut self) -> io::Result<()> {
         self.writer.write_all(b"\n")?;
 
+        if self.manual_run {
+            self.writer.write_fmt(format_args!("{}un/", 'r'.bold()))?;
+        }
+
         if self.show_done {
             self.writer.write_fmt(format_args!("{}ext/", 'n'.bold()))?;
         }
diff --git a/src/watch/terminal_event.rs b/src/watch/terminal_event.rs
index 7f7ebe0..6d790b7 100644
--- a/src/watch/terminal_event.rs
+++ b/src/watch/terminal_event.rs
@@ -4,6 +4,7 @@ use std::sync::mpsc::Sender;
 use super::WatchEvent;
 
 pub enum InputEvent {
+    Run,
     Next,
     Hint,
     List,
@@ -11,7 +12,7 @@ pub enum InputEvent {
     Unrecognized(String),
 }
 
-pub fn terminal_event_handler(tx: Sender<WatchEvent>) {
+pub fn terminal_event_handler(tx: Sender<WatchEvent>, manual_run: bool) {
     let mut input = String::with_capacity(8);
 
     let last_input_event = loop {
@@ -43,6 +44,7 @@ pub fn terminal_event_handler(tx: Sender<WatchEvent>) {
                             "h" | "hint" => InputEvent::Hint,
                             "l" | "list" => break InputEvent::List,
                             "q" | "quit" => break InputEvent::Quit,
+                            "r" | "run" if manual_run => InputEvent::Run,
                             _ => InputEvent::Unrecognized(input.clone()),
                         };