diff --git a/src/Partition.hxx b/src/Partition.hxx
index 7a57f741e..bbcf4f546 100644
--- a/src/Partition.hxx
+++ b/src/Partition.hxx
@@ -63,12 +63,12 @@ struct Partition final : private PlayerListener, private MixerListener {
 		return playlist.AppendURI(pc, loader, uri_utf8, error);
 	}
 
-	PlaylistResult DeletePosition(unsigned position) {
-		return playlist.DeletePosition(pc, position);
+	void DeletePosition(unsigned position) {
+		playlist.DeletePosition(pc, position);
 	}
 
-	PlaylistResult DeleteId(unsigned id) {
-		return playlist.DeleteId(pc, id);
+	void DeleteId(unsigned id) {
+		playlist.DeleteId(pc, id);
 	}
 
 	/**
@@ -77,8 +77,8 @@ struct Partition final : private PlayerListener, private MixerListener {
 	 * @param start the position of the first song to delete
 	 * @param end the position after the last song to delete
 	 */
-	PlaylistResult DeleteRange(unsigned start, unsigned end) {
-		return playlist.DeleteRange(pc, start, end);
+	void DeleteRange(unsigned start, unsigned end) {
+		playlist.DeleteRange(pc, start, end);
 	}
 
 #ifdef ENABLE_DATABASE
@@ -93,53 +93,50 @@ struct Partition final : private PlayerListener, private MixerListener {
 		playlist.Shuffle(pc, start, end);
 	}
 
-	PlaylistResult MoveRange(unsigned start, unsigned end, int to) {
-		return playlist.MoveRange(pc, start, end, to);
+	void MoveRange(unsigned start, unsigned end, int to) {
+		playlist.MoveRange(pc, start, end, to);
 	}
 
-	PlaylistResult MoveId(unsigned id, int to) {
-		return playlist.MoveId(pc, id, to);
+	void MoveId(unsigned id, int to) {
+		playlist.MoveId(pc, id, to);
 	}
 
-	PlaylistResult SwapPositions(unsigned song1, unsigned song2) {
-		return playlist.SwapPositions(pc, song1, song2);
+	void SwapPositions(unsigned song1, unsigned song2) {
+		playlist.SwapPositions(pc, song1, song2);
 	}
 
-	PlaylistResult SwapIds(unsigned id1, unsigned id2) {
-		return playlist.SwapIds(pc, id1, id2);
+	void SwapIds(unsigned id1, unsigned id2) {
+		playlist.SwapIds(pc, id1, id2);
 	}
 
-	PlaylistResult SetPriorityRange(unsigned start_position,
-					unsigned end_position,
-					uint8_t priority) {
-		return playlist.SetPriorityRange(pc,
-						 start_position, end_position,
-						 priority);
+	void SetPriorityRange(unsigned start_position, unsigned end_position,
+			      uint8_t priority) {
+		playlist.SetPriorityRange(pc, start_position, end_position,
+					  priority);
 	}
 
-	PlaylistResult SetPriorityId(unsigned song_id,
-				     uint8_t priority) {
-		return playlist.SetPriorityId(pc, song_id, priority);
+	void SetPriorityId(unsigned song_id, uint8_t priority) {
+		playlist.SetPriorityId(pc, song_id, priority);
 	}
 
 	void Stop() {
 		playlist.Stop(pc);
 	}
 
-	PlaylistResult PlayPosition(int position) {
-		return playlist.PlayPosition(pc, position);
+	void PlayPosition(int position) {
+		playlist.PlayPosition(pc, position);
 	}
 
-	PlaylistResult PlayId(int id) {
-		return playlist.PlayId(pc, id);
+	void PlayId(int id) {
+		playlist.PlayId(pc, id);
 	}
 
 	void PlayNext() {
-		return playlist.PlayNext(pc);
+		playlist.PlayNext(pc);
 	}
 
 	void PlayPrevious() {
-		return playlist.PlayPrevious(pc);
+		playlist.PlayPrevious(pc);
 	}
 
 	bool SeekSongPosition(unsigned song_position,
diff --git a/src/PlaylistError.hxx b/src/PlaylistError.hxx
index 9b80d9045..4e62a199b 100644
--- a/src/PlaylistError.hxx
+++ b/src/PlaylistError.hxx
@@ -20,6 +20,8 @@
 #ifndef MPD_PLAYLIST_ERROR_HXX
 #define MPD_PLAYLIST_ERROR_HXX
 
+#include <stdexcept>
+
 class Domain;
 
 enum class PlaylistResult {
@@ -37,4 +39,26 @@ enum class PlaylistResult {
 
 extern const Domain playlist_domain;
 
+class PlaylistError : public std::runtime_error {
+	PlaylistResult code;
+
+public:
+	PlaylistError(PlaylistResult _code, const char *msg)
+		:std::runtime_error(msg), code(_code) {}
+
+	PlaylistResult GetCode() const {
+		return code;
+	}
+
+	static PlaylistError NoSuchSong() {
+		return PlaylistError(PlaylistResult::BAD_RANGE,
+				     "No such song");
+	}
+
+	static PlaylistError BadRange() {
+		return PlaylistError(PlaylistResult::BAD_RANGE,
+				     "Bad song index");
+	}
+};
+
 #endif
diff --git a/src/command/CommandError.cxx b/src/command/CommandError.cxx
index 812cb11b2..93fe2cc94 100644
--- a/src/command/CommandError.cxx
+++ b/src/command/CommandError.cxx
@@ -165,6 +165,8 @@ PrintError(Response &r, const std::exception &e)
 
 	try {
 		throw e;
+	} catch (const PlaylistError &pe) {
+		r.Error(ToAck(pe.GetCode()), pe.what());
 	} catch (const std::system_error &) {
 		r.Error(ACK_ERROR_SYSTEM, e.what());
 	} catch (...) {
diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx
index 9cfd58a93..54ef4f3e7 100644
--- a/src/command/PlayerCommands.cxx
+++ b/src/command/PlayerCommands.cxx
@@ -63,8 +63,8 @@ handle_play(Client &client, Request args, Response &r)
 	if (!args.ParseOptional(0, song, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result = client.partition.PlayPosition(song);
-	return print_playlist_result(r, result);
+	client.partition.PlayPosition(song);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -74,8 +74,8 @@ handle_playid(Client &client, Request args, Response &r)
 	if (!args.ParseOptional(0, id, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result = client.partition.PlayId(id);
-	return print_playlist_result(r, result);
+	client.partition.PlayId(id);
+	return CommandResult::OK;
 }
 
 CommandResult
diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx
index 1d02c8191..75b3a6057 100644
--- a/src/command/QueueCommands.cxx
+++ b/src/command/QueueCommands.cxx
@@ -132,12 +132,13 @@ handle_addid(Client &client, Request args, Response &r)
 		if (!args.Parse(1, to, r))
 			return CommandResult::ERROR;
 
-		PlaylistResult result = client.partition.MoveId(added_id, to);
-		if (result != PlaylistResult::SUCCESS) {
-			CommandResult ret =
-				print_playlist_result(r, result);
+		try {
+			client.partition.MoveId(added_id, to);
+			return CommandResult::OK;
+		} catch (...) {
+			/* rollback */
 			client.partition.DeleteId(added_id);
-			return ret;
+			throw;
 		}
 	}
 
@@ -205,8 +206,8 @@ handle_delete(Client &client, Request args, Response &r)
 	if (!args.Parse(0, range, r))
 		return CommandResult::ERROR;
 
-	auto result = client.partition.DeleteRange(range.start, range.end);
-	return print_playlist_result(r, result);
+	client.partition.DeleteRange(range.start, range.end);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -216,8 +217,8 @@ handle_deleteid(Client &client, Request args, Response &r)
 	if (!args.Parse(0, id, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result = client.partition.DeleteId(id);
-	return print_playlist_result(r, result);
+	client.partition.DeleteId(id);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -351,12 +352,8 @@ handle_prio(Client &client, Request args, Response &r)
 		if (!ParseCommandArg(r, range, i))
 			return CommandResult::ERROR;
 
-		PlaylistResult result =
-			client.partition.SetPriorityRange(range.start,
-							  range.end,
-							  priority);
-		if (result != PlaylistResult::SUCCESS)
-			return print_playlist_result(r, result);
+		client.partition.SetPriorityRange(range.start, range.end,
+						  priority);
 	}
 
 	return CommandResult::OK;
@@ -374,10 +371,7 @@ handle_prioid(Client &client, Request args, Response &r)
 		if (!ParseCommandArg(r, song_id, i))
 			return CommandResult::ERROR;
 
-		PlaylistResult result =
-			client.partition.SetPriorityId(song_id, priority);
-		if (result != PlaylistResult::SUCCESS)
-			return print_playlist_result(r, result);
+		client.partition.SetPriorityId(song_id, priority);
 	}
 
 	return CommandResult::OK;
@@ -392,9 +386,8 @@ handle_move(Client &client, Request args, Response &r)
 	if (!args.Parse(0, range, r) || !args.Parse(1, to, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result =
-		client.partition.MoveRange(range.start, range.end, to);
-	return print_playlist_result(r, result);
+	client.partition.MoveRange(range.start, range.end, to);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -405,8 +398,8 @@ handle_moveid(Client &client, Request args, Response &r)
 	if (!args.Parse(0, id, r) || !args.Parse(1, to, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result = client.partition.MoveId(id, to);
-	return print_playlist_result(r, result);
+	client.partition.MoveId(id, to);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -416,9 +409,8 @@ handle_swap(Client &client, Request args, Response &r)
 	if (!args.Parse(0, song1, r) || !args.Parse(1, song2, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result =
-		client.partition.SwapPositions(song1, song2);
-	return print_playlist_result(r, result);
+	client.partition.SwapPositions(song1, song2);
+	return CommandResult::OK;
 }
 
 CommandResult
@@ -428,6 +420,6 @@ handle_swapid(Client &client, Request args, Response &r)
 	if (!args.Parse(0, id1, r) || !args.Parse(1, id2, r))
 		return CommandResult::ERROR;
 
-	PlaylistResult result = client.partition.SwapIds(id1, id2);
-	return print_playlist_result(r, result);
+	client.partition.SwapIds(id1, id2);
+	return CommandResult::OK;
 }
diff --git a/src/queue/Playlist.hxx b/src/queue/Playlist.hxx
index fb78e9373..dcd55c2ef 100644
--- a/src/queue/Playlist.hxx
+++ b/src/queue/Playlist.hxx
@@ -214,15 +214,13 @@ protected:
 			    unsigned song, const DetachedSong **queued_p);
 
 public:
-	PlaylistResult DeletePosition(PlayerControl &pc,
-				      unsigned position);
+	void DeletePosition(PlayerControl &pc, unsigned position);
 
-	PlaylistResult DeleteOrder(PlayerControl &pc,
-				   unsigned order) {
-		return DeletePosition(pc, queue.OrderToPosition(order));
+	void DeleteOrder(PlayerControl &pc, unsigned order) {
+		DeletePosition(pc, queue.OrderToPosition(order));
 	}
 
-	PlaylistResult DeleteId(PlayerControl &pc, unsigned id);
+	void DeleteId(PlayerControl &pc, unsigned id);
 
 	/**
 	 * Deletes a range of songs from the playlist.
@@ -230,31 +228,27 @@ public:
 	 * @param start the position of the first song to delete
 	 * @param end the position after the last song to delete
 	 */
-	PlaylistResult DeleteRange(PlayerControl &pc,
-				   unsigned start, unsigned end);
+	void DeleteRange(PlayerControl &pc, unsigned start, unsigned end);
 
 	void DeleteSong(PlayerControl &pc, const char *uri);
 
 	void Shuffle(PlayerControl &pc, unsigned start, unsigned end);
 
-	PlaylistResult MoveRange(PlayerControl &pc,
-				 unsigned start, unsigned end, int to);
+	void MoveRange(PlayerControl &pc, unsigned start,
+		       unsigned end, int to);
 
-	PlaylistResult MoveId(PlayerControl &pc, unsigned id, int to);
+	void MoveId(PlayerControl &pc, unsigned id, int to);
 
-	PlaylistResult SwapPositions(PlayerControl &pc,
-				     unsigned song1, unsigned song2);
+	void SwapPositions(PlayerControl &pc, unsigned song1, unsigned song2);
 
-	PlaylistResult SwapIds(PlayerControl &pc,
-			       unsigned id1, unsigned id2);
+	void SwapIds(PlayerControl &pc, unsigned id1, unsigned id2);
 
-	PlaylistResult SetPriorityRange(PlayerControl &pc,
-					unsigned start_position,
-					unsigned end_position,
-					uint8_t priority);
+	void SetPriorityRange(PlayerControl &pc,
+			      unsigned start_position, unsigned end_position,
+			      uint8_t priority);
 
-	PlaylistResult SetPriorityId(PlayerControl &pc,
-				     unsigned song_id, uint8_t priority);
+	void SetPriorityId(PlayerControl &pc,
+			   unsigned song_id, uint8_t priority);
 
 	/**
 	 * Sets the start_time and end_time attributes on the song
@@ -270,11 +264,11 @@ public:
 
 	void Stop(PlayerControl &pc);
 
-	PlaylistResult PlayPosition(PlayerControl &pc, int position);
+	void PlayPosition(PlayerControl &pc, int position);
 
 	void PlayOrder(PlayerControl &pc, int order);
 
-	PlaylistResult PlayId(PlayerControl &pc, int id);
+	void PlayId(PlayerControl &pc, int id);
 
 	void PlayNext(PlayerControl &pc);
 
diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx
index 7ed19fbfb..084e44558 100644
--- a/src/queue/PlaylistControl.cxx
+++ b/src/queue/PlaylistControl.cxx
@@ -56,7 +56,7 @@ playlist::Stop(PlayerControl &pc)
 	}
 }
 
-PlaylistResult
+void
 playlist::PlayPosition(PlayerControl &pc, int song)
 {
 	pc.LockClearError();
@@ -66,13 +66,13 @@ playlist::PlayPosition(PlayerControl &pc, int song)
 		/* play any song ("current" song, or the first song */
 
 		if (queue.IsEmpty())
-			return PlaylistResult::SUCCESS;
+			return;
 
 		if (playing) {
 			/* already playing: unpause playback, just in
 			   case it was paused, and return */
 			pc.LockSetPause(false);
-			return PlaylistResult::SUCCESS;
+			return;
 		}
 
 		/* select a song: "current" song, or the first one */
@@ -80,7 +80,7 @@ playlist::PlayPosition(PlayerControl &pc, int song)
 			? current
 			: 0;
 	} else if (!queue.IsValidPosition(song))
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	if (queue.random) {
 		if (song >= 0)
@@ -103,18 +103,19 @@ playlist::PlayPosition(PlayerControl &pc, int song)
 	error_count = 0;
 
 	PlayOrder(pc, i);
-	return PlaylistResult::SUCCESS;
 }
 
-PlaylistResult
+void
 playlist::PlayId(PlayerControl &pc, int id)
 {
-	if (id == -1)
-		return PlayPosition(pc, id);
+	if (id == -1) {
+		PlayPosition(pc, id);
+		return;
+	}
 
 	int song = queue.IdToPosition(id);
 	if (song < 0)
-		return PlaylistResult::NO_SUCH_SONG;
+		throw PlaylistError::NoSuchSong();
 
 	return PlayPosition(pc, song);
 }
diff --git a/src/queue/PlaylistEdit.cxx b/src/queue/PlaylistEdit.cxx
index fe09bf1e4..92d0d050a 100644
--- a/src/queue/PlaylistEdit.cxx
+++ b/src/queue/PlaylistEdit.cxx
@@ -138,11 +138,11 @@ playlist::AppendURI(PlayerControl &pc, const SongLoader &loader,
 	return result;
 }
 
-PlaylistResult
+void
 playlist::SwapPositions(PlayerControl &pc, unsigned song1, unsigned song2)
 {
 	if (!queue.IsValidPosition(song1) || !queue.IsValidPosition(song2))
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	const DetachedSong *const queued_song = GetQueuedSong();
 
@@ -165,35 +165,33 @@ playlist::SwapPositions(PlayerControl &pc, unsigned song1, unsigned song2)
 
 	UpdateQueuedSong(pc, queued_song);
 	OnModified();
-
-	return PlaylistResult::SUCCESS;
 }
 
-PlaylistResult
+void
 playlist::SwapIds(PlayerControl &pc, unsigned id1, unsigned id2)
 {
 	int song1 = queue.IdToPosition(id1);
 	int song2 = queue.IdToPosition(id2);
 
 	if (song1 < 0 || song2 < 0)
-		return PlaylistResult::NO_SUCH_SONG;
+		throw PlaylistError::NoSuchSong();
 
-	return SwapPositions(pc, song1, song2);
+	SwapPositions(pc, song1, song2);
 }
 
-PlaylistResult
+void
 playlist::SetPriorityRange(PlayerControl &pc,
 			   unsigned start, unsigned end,
 			   uint8_t priority)
 {
 	if (start >= GetLength())
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	if (end > GetLength())
 		end = GetLength();
 
 	if (start >= end)
-		return PlaylistResult::SUCCESS;
+		return;
 
 	/* remember "current" and "queued" */
 
@@ -211,21 +209,17 @@ playlist::SetPriorityRange(PlayerControl &pc,
 
 	UpdateQueuedSong(pc, queued_song);
 	OnModified();
-
-	return PlaylistResult::SUCCESS;
 }
 
-PlaylistResult
+void
 playlist::SetPriorityId(PlayerControl &pc,
 			unsigned song_id, uint8_t priority)
 {
 	int song_position = queue.IdToPosition(song_id);
 	if (song_position < 0)
-		return PlaylistResult::NO_SUCH_SONG;
-
-	return SetPriorityRange(pc, song_position, song_position + 1,
-				priority);
+		throw PlaylistError::NoSuchSong();
 
+	SetPriorityRange(pc, song_position, song_position + 1, priority);
 }
 
 void
@@ -272,11 +266,11 @@ playlist::DeleteInternal(PlayerControl &pc,
 		current--;
 }
 
-PlaylistResult
+void
 playlist::DeletePosition(PlayerControl &pc, unsigned song)
 {
 	if (song >= queue.GetLength())
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	const DetachedSong *queued_song = GetQueuedSong();
 
@@ -284,21 +278,19 @@ playlist::DeletePosition(PlayerControl &pc, unsigned song)
 
 	UpdateQueuedSong(pc, queued_song);
 	OnModified();
-
-	return PlaylistResult::SUCCESS;
 }
 
-PlaylistResult
+void
 playlist::DeleteRange(PlayerControl &pc, unsigned start, unsigned end)
 {
 	if (start >= queue.GetLength())
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	if (end > queue.GetLength())
 		end = queue.GetLength();
 
 	if (start >= end)
-		return PlaylistResult::SUCCESS;
+		return;
 
 	const DetachedSong *queued_song = GetQueuedSong();
 
@@ -308,18 +300,16 @@ playlist::DeleteRange(PlayerControl &pc, unsigned start, unsigned end)
 
 	UpdateQueuedSong(pc, queued_song);
 	OnModified();
-
-	return PlaylistResult::SUCCESS;
 }
 
-PlaylistResult
+void
 playlist::DeleteId(PlayerControl &pc, unsigned id)
 {
 	int song = queue.IdToPosition(id);
 	if (song < 0)
-		return PlaylistResult::NO_SUCH_SONG;
+		throw PlaylistError::NoSuchSong();
 
-	return DeletePosition(pc, song);
+	DeletePosition(pc, song);
 }
 
 void
@@ -330,19 +320,19 @@ playlist::DeleteSong(PlayerControl &pc, const char *uri)
 			DeletePosition(pc, i);
 }
 
-PlaylistResult
+void
 playlist::MoveRange(PlayerControl &pc, unsigned start, unsigned end, int to)
 {
 	if (!queue.IsValidPosition(start) || !queue.IsValidPosition(end - 1))
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	if ((to >= 0 && to + end - start - 1 >= GetLength()) ||
 	    (to < 0 && unsigned(abs(to)) > GetLength()))
-		return PlaylistResult::BAD_RANGE;
+		throw PlaylistError::BadRange();
 
 	if ((int)start == to)
 		/* nothing happens */
-		return PlaylistResult::SUCCESS;
+		return;
 
 	const DetachedSong *const queued_song = GetQueuedSong();
 
@@ -355,11 +345,11 @@ playlist::MoveRange(PlayerControl &pc, unsigned start, unsigned end, int to)
 		if (currentSong < 0)
 			/* can't move relative to current song,
 			   because there is no current song */
-			return PlaylistResult::BAD_RANGE;
+			throw PlaylistError::BadRange();
 
 		if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
 			/* no-op, can't be moved to offset of itself */
-			return PlaylistResult::SUCCESS;
+			return;
 		to = (currentSong + abs(to)) % GetLength();
 		if (start < (unsigned)to)
 			to--;
@@ -379,18 +369,16 @@ playlist::MoveRange(PlayerControl &pc, unsigned start, unsigned end, int to)
 
 	UpdateQueuedSong(pc, queued_song);
 	OnModified();
-
-	return PlaylistResult::SUCCESS;
 }
 
-PlaylistResult
+void
 playlist::MoveId(PlayerControl &pc, unsigned id1, int to)
 {
 	int song = queue.IdToPosition(id1);
 	if (song < 0)
-		return PlaylistResult::NO_SUCH_SONG;
+		throw PlaylistError::NoSuchSong();
 
-	return MoveRange(pc, song, song + 1, to);
+	MoveRange(pc, song, song + 1, to);
 }
 
 void