From a4530cdaee4eee1b6a7d50a05bcc6291d0e256b6 Mon Sep 17 00:00:00 2001 From: i10416 Date: Tue, 21 Jul 2026 02:42:38 +0900 Subject: [PATCH] fix: avoid NPE when a relative path is provided to Watcher::watch Before this commit, `Watcher::watch` throws NPE when a path is relative path without a parent. This commit updates watchFile methods to defensively convert a path into absolute path and adds a test to confirm it won't throw NPE. It supersedes https://github.com/typelevel/fs2/pull/3553 by SanjayUG --- .../main/scala/fs2/io/DeprecatedWatcher.scala | 2 +- .../src/main/scala/fs2/io/file/Watcher.scala | 2 +- .../test/scala/fs2/io/file/WatcherSuite.scala | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/io/jvm-native/src/main/scala/fs2/io/DeprecatedWatcher.scala b/io/jvm-native/src/main/scala/fs2/io/DeprecatedWatcher.scala index 500dd8fb47..ccf8ed3abd 100644 --- a/io/jvm-native/src/main/scala/fs2/io/DeprecatedWatcher.scala +++ b/io/jvm-native/src/main/scala/fs2/io/DeprecatedWatcher.scala @@ -263,7 +263,7 @@ object Watcher { types: Seq[Watcher.EventType], modifiers: Seq[WatchEvent.Modifier] ): F[F[Unit]] = - registerUntracked(path.getParent, types, modifiers).flatMap(key => + registerUntracked(path.toAbsolutePath.getParent, types, modifiers).flatMap(key => track( Registration( path, diff --git a/io/jvm-native/src/main/scala/fs2/io/file/Watcher.scala b/io/jvm-native/src/main/scala/fs2/io/file/Watcher.scala index f688d4e212..353383b30b 100644 --- a/io/jvm-native/src/main/scala/fs2/io/file/Watcher.scala +++ b/io/jvm-native/src/main/scala/fs2/io/file/Watcher.scala @@ -288,7 +288,7 @@ object Watcher { types: Seq[Watcher.EventType], modifiers: Seq[WatchEvent.Modifier] ): F[F[Unit]] = - registerUntracked(path.toNioPath.getParent, types, modifiers).flatMap(key => + registerUntracked(path.absolute.toNioPath.getParent(), types, modifiers).flatMap(key => track( Registration( path.toNioPath, diff --git a/io/jvm/src/test/scala/fs2/io/file/WatcherSuite.scala b/io/jvm/src/test/scala/fs2/io/file/WatcherSuite.scala index 93af3c29f5..3d5b92cafb 100644 --- a/io/jvm/src/test/scala/fs2/io/file/WatcherSuite.scala +++ b/io/jvm/src/test/scala/fs2/io/file/WatcherSuite.scala @@ -33,6 +33,22 @@ import java.nio.file.WatchEvent class WatcherSuite extends Fs2Suite with BaseFileSuite { override def munitIOTimeout = 1.minute + group("support watching a file") { + test("for relative paths without throwing NPE") { + val mkRelativePath = for { + cwd <- Files[IO].currentWorkingDirectory.toResource + file <- Files[IO].tempFile(Some(cwd), "", ".tmp", None) + relPath = cwd.relativize(file) + } yield relPath + mkRelativePath + .both(Watcher.default[IO]) + .use { case (path, watcher) => + watcher.watch(path) + } + .void + } + } + group("supports watching a file") { test("for modifications") { Stream