diff --git a/Makefile.am b/Makefile.am
index ebe358d6d..2720e4ad9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -600,7 +600,7 @@ libfs_a_SOURCES = \
 	src/fs/Glob.hxx \
 	src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \
 	src/fs/CheckFile.cxx src/fs/CheckFile.hxx \
-	src/fs/DirectoryReader.hxx
+	src/fs/DirectoryReader.cxx src/fs/DirectoryReader.hxx
 libfs_a_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS)
 
 if ENABLE_ZLIB
diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx
index 0bd17b2a2..196641289 100644
--- a/src/PlaylistFile.cxx
+++ b/src/PlaylistFile.cxx
@@ -174,7 +174,7 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
 }
 
 PlaylistVector
-ListPlaylistFiles(Error &error)
+ListPlaylistFiles()
 {
 	PlaylistVector list;
 
@@ -182,10 +182,6 @@ ListPlaylistFiles(Error &error)
 	assert(!parent_path_fs.IsNull());
 
 	DirectoryReader reader(parent_path_fs);
-	if (reader.HasFailed()) {
-		error.SetErrno();
-		return list;
-	}
 
 	PlaylistInfo info;
 	while (reader.ReadEntry()) {
diff --git a/src/PlaylistFile.hxx b/src/PlaylistFile.hxx
index 150d35710..5a12f314b 100644
--- a/src/PlaylistFile.hxx
+++ b/src/PlaylistFile.hxx
@@ -50,11 +50,10 @@ AllocatedPath
 spl_map_to_fs(const char *name_utf8);
 
 /**
- * Returns a list of stored_playlist_info struct pointers.  Returns
- * nullptr if an error occurred.
+ * Returns a list of stored_playlist_info struct pointers.
  */
 PlaylistVector
-ListPlaylistFiles(Error &error);
+ListPlaylistFiles();
 
 PlaylistFileContents
 LoadPlaylistFile(const char *utf8path);
diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx
index 486c00d89..3d7f3356a 100644
--- a/src/command/FileCommands.cxx
+++ b/src/command/FileCommands.cxx
@@ -70,15 +70,9 @@ skip_path(Path name_fs)
 #endif
 
 CommandResult
-handle_listfiles_local(Response &r,
-		       const char *path_utf8, Path path_fs)
+handle_listfiles_local(Response &r, Path path_fs)
 {
 	DirectoryReader reader(path_fs);
-	if (reader.HasFailed()) {
-		Error error;
-		error.FormatErrno("Failed to open '%s'", path_utf8);
-		return print_error(r, error);
-	}
 
 	while (reader.ReadEntry()) {
 		const Path name_fs = reader.GetEntry();
diff --git a/src/command/FileCommands.hxx b/src/command/FileCommands.hxx
index 9c6631df5..92cf59367 100644
--- a/src/command/FileCommands.hxx
+++ b/src/command/FileCommands.hxx
@@ -28,8 +28,7 @@ class Response;
 class Path;
 
 CommandResult
-handle_listfiles_local(Response &response,
-		       const char *path_utf8, Path path_fs);
+handle_listfiles_local(Response &response, Path path_fs);
 
 CommandResult
 handle_read_comments(Client &client, Request request, Response &response);
diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx
index 0b298f33e..3824e5d55 100644
--- a/src/command/OtherCommands.cxx
+++ b/src/command/OtherCommands.cxx
@@ -49,6 +49,7 @@
 #include "Partition.hxx"
 #include "Instance.hxx"
 #include "Idle.hxx"
+#include "Log.hxx"
 
 #ifdef ENABLE_DATABASE
 #include "DatabaseCommands.hxx"
@@ -161,8 +162,7 @@ handle_listfiles(Client &client, Request args, Response &r)
 
 	case LocatedUri::Type::PATH:
 		/* list local directory */
-		return handle_listfiles_local(r, located_uri.canonical_uri,
-					      located_uri.path);
+		return handle_listfiles_local(r, located_uri.path);
 	}
 
 	gcc_unreachable();
@@ -197,9 +197,11 @@ handle_lsinfo_relative(Client &client, Response &r, const char *uri)
 #endif
 
 	if (isRootDirectory(uri)) {
-		Error error;
-		const auto &list = ListPlaylistFiles(error);
-		print_spl_list(r, list);
+		try {
+			print_spl_list(r, ListPlaylistFiles());
+		} catch (const std::exception &e) {
+			LogError(e);
+		}
 	} else {
 #ifndef ENABLE_DATABASE
 		r.Error(ACK_ERROR_NO_EXIST, "No database");
diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx
index 10135467b..1c5de81fe 100644
--- a/src/command/PlaylistCommands.cxx
+++ b/src/command/PlaylistCommands.cxx
@@ -198,11 +198,6 @@ CommandResult
 handle_listplaylists(gcc_unused Client &client, gcc_unused Request args,
 		     Response &r)
 {
-	Error error;
-	const auto list = ListPlaylistFiles(error);
-	if (list.empty() && error.IsDefined())
-		return print_error(r, error);
-
-	print_spl_list(r, list);
+	print_spl_list(r, ListPlaylistFiles());
 	return CommandResult::OK;
 }
diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx
index f1f2f9bcc..a374af073 100644
--- a/src/db/update/Walk.cxx
+++ b/src/db/update/Walk.cxx
@@ -218,7 +218,7 @@ void
 UpdateWalk::UpdateDirectoryChild(Directory &directory,
 				 const ExcludeList &exclude_list,
 				 const char *name, const StorageFileInfo &info)
-{
+try {
 	assert(strchr(name, '/') == nullptr);
 
 	if (info.IsRegular()) {
@@ -242,6 +242,8 @@ UpdateWalk::UpdateDirectoryChild(Directory &directory,
 		FormatDebug(update_domain,
 			    "%s is not a directory, archive or music", name);
 	}
+} catch (const std::exception &e) {
+	LogError(e);
 }
 
 /* we don't look at "." / ".." nor files with newlines in their name */
@@ -455,7 +457,7 @@ UpdateWalk::DirectoryMakeUriParentChecked(Directory &root, const char *uri)
 
 inline void
 UpdateWalk::UpdateUri(Directory &root, const char *uri)
-{
+try {
 	Directory *parent = DirectoryMakeUriParentChecked(root, uri);
 	if (parent == nullptr)
 		return;
@@ -476,6 +478,8 @@ UpdateWalk::UpdateUri(Directory &root, const char *uri)
 	ExcludeList exclude_list;
 
 	UpdateDirectoryChild(*parent, exclude_list, name, info);
+} catch (const std::exception &e) {
+	LogError(e);
 }
 
 bool
diff --git a/src/fs/CheckFile.cxx b/src/fs/CheckFile.cxx
index e900fe9af..87064604a 100644
--- a/src/fs/CheckFile.cxx
+++ b/src/fs/CheckFile.cxx
@@ -25,6 +25,7 @@
 #include "Path.hxx"
 #include "AllocatedPath.hxx"
 #include "DirectoryReader.hxx"
+#include "system/Error.hxx"
 
 #include <errno.h>
 #include <sys/stat.h>
@@ -58,11 +59,12 @@ CheckDirectoryReadable(Path path_fs)
 	}
 #endif
 
-	const DirectoryReader reader(path_fs);
-	if (reader.HasFailed() && errno == EACCES) {
-		const auto path_utf8 = path_fs.ToUTF8();
-		FormatError(config_domain,
-			    "No permission to read directory: %s",
-			    path_utf8.c_str());
+	try {
+		const DirectoryReader reader(path_fs);
+	} catch (const std::system_error &e) {
+		if (IsAccessDenied(e))
+			FormatError(config_domain,
+				    "No permission to read directory: %s",
+				    path_fs.ToUTF8().c_str());
 	}
 }
diff --git a/src/fs/DirectoryReader.cxx b/src/fs/DirectoryReader.cxx
new file mode 100644
index 000000000..700cb2b2d
--- /dev/null
+++ b/src/fs/DirectoryReader.cxx
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DirectoryReader.hxx"
+#include "system/Error.hxx"
+
+#ifdef WIN32
+
+DirectoryReader::DirectoryReader(Path dir)
+	:handle(FindFirstFile(MakeWildcardPath(dir.c_str()), &data))
+{
+	if (handle == INVALID_HANDLE_VALUE)
+		throw FormatLastError("Failed to open %s", dir.c_str());
+}
+
+#else
+
+DirectoryReader::DirectoryReader(Path dir)
+	:dirp(opendir(dir.c_str()))
+{
+	if (dirp == nullptr)
+		throw FormatErrno("Failed to open %s", dir.c_str());
+}
+
+#endif
diff --git a/src/fs/DirectoryReader.hxx b/src/fs/DirectoryReader.hxx
index 88af5b184..b4c140ada 100644
--- a/src/fs/DirectoryReader.hxx
+++ b/src/fs/DirectoryReader.hxx
@@ -61,10 +61,10 @@ class DirectoryReader {
 public:
 	/**
 	 * Creates new directory reader for the specified #dir.
+	 *
+	 * Throws std::system_error on error.
 	 */
-	explicit DirectoryReader(Path dir)
-		:handle(FindFirstFile(MakeWildcardPath(dir.c_str()), &data)) {
-	}
+	explicit DirectoryReader(Path dir);
 
 	DirectoryReader(const DirectoryReader &other) = delete;
 	DirectoryReader &operator=(const DirectoryReader &other) = delete;
@@ -73,15 +73,7 @@ public:
 	 * Destroys this instance.
 	 */
 	~DirectoryReader() {
-		if (!HasFailed())
-			FindClose(handle);
-	}
-
-	/**
-	 * Checks if directory failed to open.
-	 */
-	bool HasFailed() const {
-		return handle == INVALID_HANDLE_VALUE;
+		FindClose(handle);
 	}
 
 	/**
@@ -118,10 +110,10 @@ class DirectoryReader {
 public:
 	/**
 	 * Creates new directory reader for the specified #dir.
+	 *
+	 * Throws std::system_error on error.
 	 */
-	explicit DirectoryReader(Path dir)
-		:dirp(opendir(dir.c_str())) {
-	}
+	explicit DirectoryReader(Path dir);
 
 	DirectoryReader(const DirectoryReader &other) = delete;
 	DirectoryReader &operator=(const DirectoryReader &other) = delete;
@@ -130,22 +122,13 @@ public:
 	 * Destroys this instance.
 	 */
 	~DirectoryReader() {
-		if (!HasFailed())
-			closedir(dirp);
-	}
-
-	/**
-	 * Checks if directory failed to open. 
-	 */
-	bool HasFailed() const {
-		return dirp == nullptr;
+		closedir(dirp);
 	}
 
 	/**
 	 * Checks if directory entry is available.
 	 */
 	bool HasEntry() const {
-		assert(!HasFailed());
 		return ent != nullptr;
 	}
 
@@ -153,7 +136,6 @@ public:
 	 * Reads next directory entry.
 	 */
 	bool ReadEntry() {
-		assert(!HasFailed());
 		ent = readdir(dirp);
 		return HasEntry();
 	}
diff --git a/src/storage/plugins/LocalStorage.cxx b/src/storage/plugins/LocalStorage.cxx
index b35161dd1..91bbb8957 100644
--- a/src/storage/plugins/LocalStorage.cxx
+++ b/src/storage/plugins/LocalStorage.cxx
@@ -41,10 +41,6 @@ public:
 	LocalDirectoryReader(AllocatedPath &&_base_fs)
 		:base_fs(std::move(_base_fs)), reader(base_fs) {}
 
-	bool HasFailed() {
-		return reader.HasFailed();
-	}
-
 	/* virtual methods from class StorageDirectoryReader */
 	const char *Read() override;
 	bool GetInfo(bool follow, StorageFileInfo &info,
@@ -160,15 +156,7 @@ LocalStorage::OpenDirectory(const char *uri_utf8, Error &error)
 	if (path_fs.IsNull())
 		return nullptr;
 
-	LocalDirectoryReader *reader =
-		new LocalDirectoryReader(std::move(path_fs));
-	if (reader->HasFailed()) {
-		error.FormatErrno("Failed to open '%s'", uri_utf8);
-		delete reader;
-		return nullptr;
-	}
-
-	return reader;
+	return new LocalDirectoryReader(std::move(path_fs));
 }
 
 gcc_pure