Repository: notify-rs/notify (2025-07-16T11:25:19Z)

Issue:
Receive Modify Event After using notify-debouncer-full
My core code as below
```
    let mut debouncer = new_debouncer(Duration::from_secs(2), None, move |result: DebounceEventResult| {
        match result {
            Ok(events) => events.iter().for_each(|event|{
                match &event.kind {
                    EventKind::Any =>{},
                    EventKind::Create(_)=>{
                        info!("收到创建事件{}",event.paths.first().unwrap().to_str().unwrap());
                    } ,
                    EventKind::Modify(_)=>{
                        info!("收到修改事件{}",event.paths.first().unwrap().to_str().unwrap());
                    } ,

                    EventKind::Remove(_)=>{
                        
                    } ,
                    EventKind::Access(_)=>{},
                    EventKind::Other=>{},
                }

            }),
            Err(errors) => errors.iter().for_each(|err| error!("{err:?}")),
        }
    }).unwrap();
```

when I create a file , I will reveive modify event;
```
INFO [file_listener::watch::watch] 收到创建事件E:\test\filelisten\src\fddfd - 副本 (5).xml
INFO [file_listener::watch::watch] 收到修改事件E:\test\filelisten\src\fddfd - 副本 (5).xml
```

My cargo.toml config as below:
```[package]
name = "file-listener"
version = "0.1.0"
edition = "2021"

[dependencies]
notify-debouncer-full = "0.5.0"
notify = "8.0.0"
```


Discussion:
What operating system are you using? And how are you creating the file?

You are receiving a `Create` and then a `Modify` event. Looks good to me.
Operating system version: Windows 10 Professional

I create the file by two ways:
1: Ctrl+C and Ctrl+V on watching the dir. 
   A file named `a.xml` on the "E:\test\filelisten\src\" ,when I do Ctrl+C/V, will create a new file named `a - 副本.xml`,I will gain:
```
    INFO [file_listener] 收到创建事件E:\test\filelisten\src\a - 副本.xml
    INFO [file_listener] 收到修改事件E:\test\filelisten\src\a - 副本.xml
```

2: Copy file named 'b.xml' from the other dir, I will gain:
```
INFO [file_listener] 收到创建事件E:\test\filelisten\src\b.xml
INFO [file_listener] 收到修改事件E:\test\filelisten\src\b.xml
```
I simplify the code, and the whole code as below
main.rs
```
use std::time::Duration;

use std::path::Path;

use log::{error,info};
use notify_debouncer_full::{notify::*, new_debouncer, DebounceEventResult};
use flexi_logger::{Logger, FileSpec, Criterion, Naming, Cleanup, WriteMode, Duplicate, detailed_format};


pub fn init_log(log_lvl : &str){
    println!("日志级别[{}]",&log_lvl);
    // 初始化日志记录，配置输出到文件，设置文件大小限制和滚动日志
    let _ = Logger::try_with_str(&log_lvl)
    .unwrap()
    .log_to_file(
        FileSpec::default()
            .directory("logs") 
            .basename("file_listener") 
            .suffix("log")     
    )
    .rotate(
        Criterion::Age(flexi_logger::Age::Day), 
        Naming::Timestamps,        
        Cleanup::KeepLogFiles(3),   
    )
    .write_mode(WriteMode::BufferAndFlush) 
    .duplicate_to_stdout(Duplicate::All)  
    .format_for_files(detailed_format)    
    .start()
    .unwrap();
}

pub fn start_watch(){

    let mut debouncer = new_debouncer(Duration::from_secs(2), None, move |result: DebounceEventResult| {
        match result {
            Ok(events) => events.iter().for_each(|event|{
                match &event.kind {
                    EventKind::Any =>{},
                    EventKind::Create(_)=>{
                        info!("收到创建事件{}",event.paths.first().unwrap().to_str().unwrap());
                    } ,
                    EventKind::Modify(_)=>{
                        info!("收到修改事件{}",event.paths.first().unwrap().to_str().unwrap());
                    } ,

                    EventKind::Remove(_)=>{
                        
                    } ,
                    EventKind::Access(_)=>{},
                    EventKind::Other=>{},
                }

            }),
            Err(errors) => errors.iter().for_each(|err| error!("{err:?}")),
        }
    }).unwrap();


    debouncer.watch(Path::new("E:\\test\\filelisten\\src"), RecursiveMode::Recursive).unwrap();


    loop {
        std::thread::sleep(Duration::from_millis(100));
    }
}

fn main() {
    file_listener::log::log::init_log("info");
    // 开启监听
    start_watch();

    loop {
        std::thread::sleep(Duration::from_millis(1000));
    }
}

```
cargo.toml
```
[package]
name = "file-listener"
version = "0.1.0"
edition = "2021"

[dependencies]
log = "0.4"
flexi_logger = "0.22"
notify-debouncer-full = "0.5.0"
notify = "8.0.0"
```

in addition， I use `notify-debouncer-mini`, don't receive Modify Event, but can't gain event kind
I am also experiencing this issue.

The documentation for notify-debouncer-full states "Doesn’t emit Modify events after a Create event". However, I am still getting Modify events after Create events are fired.

I add a single file to the watched directory with this code running:

```
use config::{Config, File};
use notify_debouncer_full::{new_debouncer, DebouncedEvent, notify::*};
use std::path::Path;
use std::time::Duration;
use std::sync::mpsc::channel;

fn main() {
    let config = Config::builder().add_source(File::with_name("config")).build().expect(CONFIG_FILE_ERROR);
    let song_dir_path: String = config.get("song_dir_path").expect(CONFIG_FILE_READ_ERROR);

    let (event_sender, event_receiver) = channel();

    let mut debounced_dir_watcher = new_debouncer(Duration::from_secs(2), None, event_sender).expect(DIR_WATCHER_CREATE_ERROR);
    debounced_dir_watcher.watch(Path::new(&song_dir_path), RecursiveMode::NonRecursive).expect(DIR_WATCHER_WATCH_ERROR);

    for res in event_receiver {
        match res {
            Ok(e) => handle_directory_events(e),
            Err(e) => println!("Song dir event watcher error: {e:?}")
        }
        println!();
    }
}

fn handle_directory_events(e: Vec<DebouncedEvent>) {
    println!("Events: {:?}\n", e);
}
```

And this is the output I receive:

> [DebouncedEvent { event: Event { kind: Create(Any), paths: ["C:\\Users\\Kepler\\songs\\Stranger.mp3"], attr:tracker: None, attr:flag: None, attr:info: None, attr:source: None }, time: Instant { t: 235002.8305774s } }, DebouncedEvent { event: Event { kind: Modify(Any), paths: ["C:\\Users\\Kepler\\songs\\Stranger.mp3"], attr:tracker: None, attr:flag: None, attr:info: None, attr:source: None }, time: Instant { t: 235002.8308492s } }]



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d06d074..4b42a422 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,9 @@
 ## debouncer-full 0.6.0 (unreleased)
 - FEATURE: allow `FileIdCache` trait implementations to choose ownership of the returned file-ids
 - FEATURE: added support for the [`flume`](https://docs.rs/flume) crate
+- FIX: skip all `Modify` events right after a `Create` event, unless it's a rename event [#701]
+
+[#701]: https://github.com/notify-rs/notify/pull/701
 
 ## debouncer-mini 0.7.0 (unreleased)
 - FEATURE: added support for the [`flume`](https://docs.rs/flume) crate
diff --git a/notify-debouncer-full/src/lib.rs b/notify-debouncer-full/src/lib.rs
index 8c8c8a0e..bfb97b27 100644
--- a/notify-debouncer-full/src/lib.rs
+++ b/notify-debouncer-full/src/lib.rs
@@ -509,9 +509,15 @@ impl<T: FileIdCache> DebounceDataInner<T> {
         let path = &event.paths[0];
 
         if let Some(queue) = self.queues.get_mut(path) {
-            // skip duplicate create events and modifications right after creation
+            // Skip duplicate create events and modifications right after creation.
+            // This code relies on backends never emitting a `Modify` event with kind other than `Name` for a rename event.
             if match event.kind {
-                EventKind::Modify(ModifyKind::Data(_) | ModifyKind::Metadata(_))
+                EventKind::Modify(
+                    ModifyKind::Any
+                    | ModifyKind::Data(_)
+                    | ModifyKind::Metadata(_)
+                    | ModifyKind::Other,
+                )
                 | EventKind::Create(_) => !queue.was_created(),
                 _ => true,
             } {
@@ -639,16 +645,14 @@ pub fn new_debouncer_opt<F: DebounceEventHandler, T: Watcher, C: FileIdCache + S
         Some(v) => {
             if v > timeout {
                 return Err(Error::new(ErrorKind::Generic(format!(
-                    "Invalid tick_rate, tick rate {:?} > {:?} timeout!",
-                    v, timeout
+                    "Invalid tick_rate, tick rate {v:?} > {timeout:?} timeout!"
                 ))));
             }
             v
         }
         None => timeout.checked_div(tick_div).ok_or_else(|| {
             Error::new(ErrorKind::Generic(format!(
-                "Failed to calculate tick as {:?}/{}!",
-                timeout, tick_div
+                "Failed to calculate tick as {timeout:?}/{tick_div}!"
             )))
         })?,
     };
@@ -782,6 +786,7 @@ mod tests {
             "add_create_event_after_remove_event",
             "add_create_dir_event_twice",
             "add_event_with_no_paths_is_ok",
+            "add_modify_any_event_after_create_event",
             "add_modify_content_event_after_create_event",
             "add_rename_from_event",
             "add_rename_from_event_after_create_event",
@@ -868,7 +873,7 @@ mod tests {
             state
                 .errors
                 .iter()
-                .map(|e| format!("{:?}", e))
+                .map(|e| format!("{e:?}"))
                 .collect::<Vec<_>>(),
             expected_errors
                 .iter()


Test patch:
diff --git a/notify-debouncer-full/test_cases/add_modify_any_event_after_create_event.hjson b/notify-debouncer-full/test_cases/add_modify_any_event_after_create_event.hjson
new file mode 100644
index 00000000..de1cbbef
--- /dev/null
+++ b/notify-debouncer-full/test_cases/add_modify_any_event_after_create_event.hjson
@@ -0,0 +1,26 @@
+// https://github.com/notify-rs/notify/issues/671
+
+// Windows may emit a Create Any and then a Modify Anyevent when a file is created.
+{
+    state: {
+        queues: {
+            /watch/file: {
+                events: [
+                    { kind: "create-any", paths: ["*"] }
+                ]
+            }
+        }
+    }
+    events: [
+        { kind: "modify-any", paths: ["/watch/file"] }
+    ]
+    expected: {
+        queues: {
+            /watch/file: {
+                events: [
+                    { kind: "create-any", paths: ["*"] }
+                ]
+            }
+        }
+    }
+}