diff --git a/api/index.php b/api/index.php
index 4e2c06b9d753f2ebe86c53fc941e7c4c9d9a4f70..d85a1103c06b248399f0778018309f4bd4ec129f 100644
--- a/api/index.php
+++ b/api/index.php
@@ -34,9 +34,11 @@
 		if (!\Sessions\validate_session()) {
 			header("Content-Type: text/json");
 
-			print json_encode(array("seq" => -1,
-				"status" => 1,
-				"content" => array("error" => "NOT_LOGGED_IN")));
+			print json_encode([
+						"seq" => -1,
+						"status" => API::STATUS_ERR,
+						"content" => [ "error" => API::E_NOT_LOGGED_IN ]
+					]);
 
 			return;
 		}
diff --git a/backend.php b/backend.php
index 9bc1449d078fa05334448a4d0c2fd9be8eebeead..206d866b7c85eb2e3e0eab432e41d14607856c15 100644
--- a/backend.php
+++ b/backend.php
@@ -45,7 +45,7 @@
 	if (!empty($_SESSION["uid"])) {
 		if (!\Sessions\validate_session()) {
 			header("Content-Type: text/json");
-			print error_json(6);
+			print Errors::to_json(Errors::E_UNAUTHORIZED);
 			return;
 		}
 		UserHelper::load_user_plugins($_SESSION["uid"]);
@@ -106,7 +106,7 @@
 		if (strpos($method, "_") === 0) {
 			user_error("Refusing to invoke method $method of handler $op which starts with underscore.", E_USER_WARNING);
 			header("Content-Type: text/json");
-			print error_json(6);
+			print Errors::to_json(Errors::E_UNAUTHORIZED);
 			return;
 		}
 
@@ -130,7 +130,7 @@
 						} else {
 							user_error("Refusing to invoke method $method of handler $op which has required parameters.", E_USER_WARNING);
 							header("Content-Type: text/json");
-							print error_json(6);
+							print Errors::to_json(Errors::E_UNAUTHORIZED);
 						}
 					} else {
 						if (method_exists($handler, "catchall")) {
@@ -141,19 +141,19 @@
 					return;
 				} else {
 					header("Content-Type: text/json");
-					print error_json(6);
+					print Errors::to_json(Errors::E_UNAUTHORIZED);
 					return;
 				}
 			} else {
 				user_error("Refusing to invoke method $method of handler $op with invalid CSRF token.", E_USER_WARNING);
 				header("Content-Type: text/json");
-				print error_json(6);
+				print Errors::to_json(Errors::E_UNAUTHORIZED);
 				return;
 			}
 		}
 	}
 
 	header("Content-Type: text/json");
-	print error_json(13);
+	print Errors::to_json(Errors::E_UNKNOWN_METHOD);
 
 ?>
diff --git a/classes/api.php b/classes/api.php
index 6f3ee77dbced08c95807712ba69eb8dc3f992e9d..1b3ee7d9240c52550679084c9e3bbf82f8a9e92b 100755
--- a/classes/api.php
+++ b/classes/api.php
@@ -6,6 +6,13 @@ class API extends Handler {
 	const STATUS_OK  = 0;
 	const STATUS_ERR = 1;
 
+	const E_API_DISABLED = "API_DISABLED";
+	const E_NOT_LOGGED_IN = "NOT_LOGGED_IN";
+	const E_LOGIN_ERROR = "LOGIN_ERROR";
+	const E_INCORRECT_USAGE = "INCORRECT_USAGE";
+	const E_UNKNOWN_METHOD = "UNKNOWN_METHOD";
+	const E_OPERATION_FAILED = "E_OPERATION_FAILED";
+
 	private $seq;
 
 	private static function _param_to_bool($p) {
@@ -13,9 +20,11 @@ class API extends Handler {
 	}
 
 	private function _wrap($status, $reply) {
-		print json_encode(array("seq" => $this->seq,
-			"status" => $status,
-			"content" => $reply));
+		print json_encode([
+					"seq" => $this->seq,
+					"status" => $status,
+					"content" => $reply
+				]);
 	}
 
 	function before($method) {
@@ -23,12 +32,12 @@ class API extends Handler {
 			header("Content-Type: text/json");
 
 			if (empty($_SESSION["uid"]) && $method != "login" && $method != "isloggedin") {
-				$this->_wrap(self::STATUS_ERR, array("error" => 'NOT_LOGGED_IN'));
+				$this->_wrap(self::STATUS_ERR, array("error" => self::E_NOT_LOGGED_IN));
 				return false;
 			}
 
 			if (!empty($_SESSION["uid"]) && $method != "logout" && !get_pref('ENABLE_API_ACCESS')) {
-				$this->_wrap(self::STATUS_ERR, array("error" => 'API_DISABLED'));
+				$this->_wrap(self::STATUS_ERR, array("error" => self::E_API_DISABLED));
 				return false;
 			}
 
@@ -69,13 +78,13 @@ class API extends Handler {
 						"api_level" => self::API_LEVEL));
 				} else {                                                         // else we are not logged in
 					user_error("Failed login attempt for $login from " . UserHelper::get_user_ip(), E_USER_WARNING);
-					$this->_wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR"));
+					$this->_wrap(self::STATUS_ERR, array("error" => self::E_LOGIN_ERROR));
 				}
 			} else {
-				$this->_wrap(self::STATUS_ERR, array("error" => "API_DISABLED"));
+				$this->_wrap(self::STATUS_ERR, array("error" => self::E_API_DISABLED));
 			}
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR"));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_LOGIN_ERROR));
 			return;
 		}
 	}
@@ -221,7 +230,7 @@ class API extends Handler {
 				$this->_wrap(self::STATUS_OK, $headlines);
 			}
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_INCORRECT_USAGE));
 		}
 	}
 
@@ -281,7 +290,7 @@ class API extends Handler {
 				"updated" => $num_updated));
 
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_INCORRECT_USAGE));
 		}
 
 	}
@@ -356,7 +365,7 @@ class API extends Handler {
 
 			$this->_wrap(self::STATUS_OK, $articles);
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_INCORRECT_USAGE));
 		}
 	}
 
@@ -481,7 +490,7 @@ class API extends Handler {
 			$this->_wrap($reply[0], $reply[1]);
 
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => 'UNKNOWN_METHOD', "method" => $method));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_UNKNOWN_METHOD, "method" => $method));
 		}
 	}
 
@@ -493,7 +502,7 @@ class API extends Handler {
 		if (Article::_create_published_article($title, $url, $content, "", $_SESSION["uid"])) {
 			$this->_wrap(self::STATUS_OK, array("status" => 'OK'));
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => 'Publishing failed'));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_OPERATION_FAILED));
 		}
 	}
 
@@ -816,7 +825,7 @@ class API extends Handler {
 			Pref_Feeds::remove_feed($feed_id, $_SESSION["uid"]);
 			$this->_wrap(self::STATUS_OK, array("status" => "OK"));
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => "FEED_NOT_FOUND"));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_OPERATION_FAILED));
 		}
 	}
 
@@ -831,7 +840,7 @@ class API extends Handler {
 
 			$this->_wrap(self::STATUS_OK, array("status" => $rc));
 		} else {
-			$this->_wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE'));
+			$this->_wrap(self::STATUS_ERR, array("error" => self::E_INCORRECT_USAGE));
 		}
 	}
 
diff --git a/classes/feeds.php b/classes/feeds.php
index eaedc1aeef0e4a2044a07a604360420f03f43f2c..a38cbae976f1cc4713ad2bacbca3efbf11c8ee81 100755
--- a/classes/feeds.php
+++ b/classes/feeds.php
@@ -499,15 +499,7 @@ class Feeds extends Handler_Protected {
 		// this is parsed by handleRpcJson() on first viewfeed() to set cdm expanded, etc
 		$reply['runtime-info'] = RPC::make_runtime_info();
 
-		$reply_json = json_encode($reply);
-
-		if (!$reply_json) {
-		    $reply_json = json_encode(["error" => ["code" => 15,
-                "message" => json_last_error_msg()]]);
-        }
-
-		print $reply_json;
-
+		print json_encode($reply);
 	}
 
 	private function _generate_dashboard_feed() {
diff --git a/classes/handler/public.php b/classes/handler/public.php
index 79dff37b5fe4ee643fd24bbb3064d80b5c23c00e..42be6f7138be252e154e5f72fb686fe250db1707 100755
--- a/classes/handler/public.php
+++ b/classes/handler/public.php
@@ -240,7 +240,7 @@ class Handler_Public extends Handler {
 
 		} else {
 			header("Content-Type: text/plain; charset=utf-8");
-			print json_encode(array("error" => array("message" => "Unknown format")));
+			print "Unknown format: $format.";
 		}
 	}
 
@@ -290,7 +290,7 @@ class Handler_Public extends Handler {
 			header("Location: index.php");
 		} else {
 			header("Content-Type: text/json");
-			print error_json(6);
+			print Errors::to_json(Errors::E_UNAUTHORIZED);
 		}
 	}
 
@@ -408,7 +408,7 @@ class Handler_Public extends Handler {
 
 	function index() {
 		header("Content-Type: text/plain");
-		print error_json(13);
+		print Errors::to_json(Errors::E_UNKNOWN_METHOD);
 	}
 
 	function forgotpass() {
@@ -659,7 +659,7 @@ class Handler_Public extends Handler {
 			<div class="content">
 
 			<?php
-				@$op = clean($_REQUEST["subop"]);
+				@$op = clean($_REQUEST["subop"] ?? "");
 				$updater = new DbUpdater(Db::pdo(), Config::get(Config::DB_TYPE), SCHEMA_VERSION);
 
 				if ($op == "performupdate") {
@@ -802,17 +802,17 @@ class Handler_Public extends Handler {
 				} else {
 					user_error("PluginHandler[PUBLIC]: Requested private method '$method' of plugin '$plugin_name'.", E_USER_WARNING);
 					header("Content-Type: text/json");
-					print error_json(6);
+					print Errors::to_json(Errors::E_UNAUTHORIZED);
 				}
 			} else {
 				user_error("PluginHandler[PUBLIC]: Requested unknown method '$method' of plugin '$plugin_name'.", E_USER_WARNING);
 				header("Content-Type: text/json");
-				print error_json(13);
+				print Errors::to_json(Errors::E_UNKNOWN_METHOD);
 			}
 		} else {
 			user_error("PluginHandler[PUBLIC]: Requested method '$method' of unknown plugin '$plugin_name'.", E_USER_WARNING);
 			header("Content-Type: text/json");
-			print error_json(14);
+			print Errors::to_json(Errors::E_UNKNOWN_PLUGIN);
 		}
 	}
 
diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php
index 608f80dcbaec42002e89723ab88dad0207891da6..75b82382221dd8d3b1e536703fb585debdd52909 100644
--- a/classes/pluginhandler.php
+++ b/classes/pluginhandler.php
@@ -15,15 +15,15 @@ class PluginHandler extends Handler_Protected {
 					$plugin->$method();
 				} else {
 					user_error("Rejected ${plugin_name}->${method}(): invalid CSRF token.", E_USER_WARNING);
-					print error_json(6);
+					print Errors::to_json(Errors::E_UNAUTHORIZED);
 				}
 			} else {
 				user_error("Rejected ${plugin_name}->${method}(): unknown method.", E_USER_WARNING);
-				print error_json(13);
+				print Errors::to_json(Errors::E_UNKNOWN_METHOD);
 			}
 		} else {
 			user_error("Rejected ${plugin_name}->${method}(): unknown plugin.", E_USER_WARNING);
-			print error_json(14);
+			print Errors::to_json(Errors::E_UNKNOWN_PLUGIN);
 		}
 	}
 }
diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php
index 7c3a40647d9a7648c7a7a512e95e93e885e15284..086c52697dcace62877db26ee55edfe3b68911f2 100755
--- a/classes/pref/feeds.php
+++ b/classes/pref/feeds.php
@@ -561,8 +561,6 @@ class Pref_Feeds extends Handler_Protected {
 					"all" => $this::get_ts_languages(),
 					]
 				]);
-		} else {
-			print json_encode(["error" => "FEED_NOT_FOUND"]);
 		}
 	}
 
diff --git a/classes/pref/labels.php b/classes/pref/labels.php
index 0b826e13f30296862aa96e4e218685eb5f5dbf43..5bc094d555633d83c968d70abca3396505c3dcea 100644
--- a/classes/pref/labels.php
+++ b/classes/pref/labels.php
@@ -16,8 +16,6 @@ class Pref_Labels extends Handler_Protected {
 
 		if ($line = $sth->fetch(PDO::FETCH_ASSOC)) {
 			print json_encode($line);
-		} else {
-			print json_encode(["error" => "LABEL_NOT_FOUND"]);
 		}
 	}
 
diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php
index 7ee03c21f17f04811e7003ecf5481fac8cc8bed3..0d0dcadbc62f5bea0f88ce98993e52e4b5ed5e08 100644
--- a/classes/pref/prefs.php
+++ b/classes/pref/prefs.php
@@ -1063,7 +1063,7 @@ class Pref_Prefs extends Handler_Protected {
 			}
 		} else {
 			header("Content-Type: text/json");
-			print error_json(6);
+			print Errors::to_json(Errors::E_UNAUTHORIZED);
 		}
 	}
 
diff --git a/classes/pref/users.php b/classes/pref/users.php
index f30abe0018f194b5dec52b7f924e6f2046e23945..13f808cb30fff1e40958be0c7e05cfdd19782e16 100644
--- a/classes/pref/users.php
+++ b/classes/pref/users.php
@@ -19,8 +19,6 @@ class Pref_Users extends Handler_Administrative {
 						"user" => $row,
 						"access_level_names" => $access_level_names
 					]);
-			} else {
-				print json_encode(["error" => "USER_NOT_FOUND"]);
 			}
 		}
 
diff --git a/classes/rpc.php b/classes/rpc.php
index 52d514aae1f1cdc2267cc47072661b52899762ea..d0388a066cfb0c302b9c41df2cbd63c93b5f1d46 100755
--- a/classes/rpc.php
+++ b/classes/rpc.php
@@ -118,16 +118,22 @@ class RPC extends Handler_Protected {
 		$_SESSION["hasSandbox"] = clean($_REQUEST["hasSandbox"]) === "true";
 		$_SESSION["clientTzOffset"] = clean($_REQUEST["clientTzOffset"]);
 
-		$reply = array();
+		$error = Errors::E_SUCCESS;
 
-		$reply['error'] = sanity_check();
+		if (get_schema_version(true) != SCHEMA_VERSION) {
+			$error = Errors::E_SCHEMA_MISMATCH;
+		}
+
+		if ($error == Errors::E_SUCCESS) {
+			$reply = [];
 
-		if ($reply['error']['code'] == 0) {
 			$reply['init-params'] = $this->make_init_params();
 			$reply['runtime-info'] = $this->make_runtime_info();
-		}
 
-		print json_encode($reply);
+			print json_encode($reply);
+		} else {
+			print Errors::to_json($error);
+		}
 	}
 
 	/*function completeLabels() {
@@ -315,10 +321,7 @@ class RPC extends Handler_Protected {
 				$msg, 'client-js:' . $file, $line, $context);
 
 			echo json_encode(array("message" => "HOST_ERROR_LOGGED"));
-		} else {
-			echo json_encode(array("error" => "MESSAGE_NOT_FOUND"));
 		}
-
 	}
 
 	function checkforupdates() {
diff --git a/errors.php b/errors.php
deleted file mode 100644
index 3195c4b648e1bac5426a63ab6700cef55c707f46..0000000000000000000000000000000000000000
--- a/errors.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-	set_include_path(__DIR__ ."/include" . PATH_SEPARATOR .
-		get_include_path());
-
-	require_once "functions.php";
-
-	function get_error_types() {
-		$ERRORS[0] = "";
-
-		$ERRORS[1] = __("This program requires XmlHttpRequest " .
-				"to function properly. Your browser doesn't seem to support it.");
-
-		$ERRORS[2] = __("This program requires cookies " .
-				"to function properly. Your browser doesn't seem to support them.");
-
-		$ERRORS[3] = __("Backend sanity check failed.");
-
-		$ERRORS[4] = __("Frontend sanity check failed.");
-
-		$ERRORS[5] = __("Incorrect database schema version. &lt;a href='db-updater.php'&gt;Please update&lt;/a&gt;.");
-
-		$ERRORS[6] = __("Request not authorized.");
-
-		$ERRORS[7] = __("No operation to perform.");
-
-		$ERRORS[8] = __("Could not display feed: query failed. Please check label match syntax or local configuration.");
-
-		$ERRORS[8] = __("Denied. Your access level is insufficient to access this page.");
-
-		$ERRORS[9] = __("Configuration check failed");
-
-		$ERRORS[10] = __("Your version of MySQL is not currently supported. Please see official site for more information.");
-
-		$ERRORS[11] = "[This error is not returned by server]";
-
-		$ERRORS[12] = __("SQL escaping test failed, check your database and PHP configuration");
-
-		$ERRORS[13] = __("Method not found");
-
-		$ERRORS[14] = __("Plugin not found");
-
-		$ERRORS[15] = __("Encoding data as JSON failed");
-
-		return $ERRORS;
-	}
-
-	if ($_REQUEST['mode'] ?? "" == 'js') {
-		header("Content-Type: text/javascript; charset=UTF-8");
-
-		print "var ERRORS = [];\n";
-
-		foreach (get_error_types() as $id => $error) {
-
-			$error = preg_replace("/\n/", "", $error);
-			$error = preg_replace("/\"/", "\\\"", $error);
-
-			print "ERRORS[$id] = \"$error\";\n";
-		}
-	}
-?>
diff --git a/include/functions.php b/include/functions.php
index a698fa79dd1bd78edb7a29ac5ae91665409a0cf3..d916301fbe157c1809c540f1404f1747da819707 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -323,20 +323,6 @@
 		}
 	}
 
-	function sanity_check() {
-		require_once 'errors.php';
-		$ERRORS = get_error_types();
-
-		$error_code = 0;
-		$schema_version = get_schema_version(true);
-
-		if ($schema_version != SCHEMA_VERSION) {
-			$error_code = 5;
-		}
-
-		return array("code" => $error_code, "message" => $ERRORS[$error_code]);
-	}
-
 	function file_is_locked($filename) {
 		if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/$filename")) {
 			if (function_exists('flock')) {
@@ -533,20 +519,6 @@
 		return file_exists("themes/$theme") || file_exists("themes.local/$theme");
 	}
 
-	/**
-	 * @SuppressWarnings(unused)
-	 */
-	function error_json($code) {
-		require_once "errors.php";
-		$ERRORS = get_error_types();
-
-		@$message = $ERRORS[$code];
-
-		return json_encode(array("error" =>
-			array("code" => $code, "message" => $message)));
-
-	}
-
 	function arr_qmarks($arr) {
 		return str_repeat('?,', count($arr) - 1) . '?';
 	}
diff --git a/include/login_form.php b/include/login_form.php
index 168fe50aa4c2e2223ae68071a83613137f351ef3..06bf5747028d45d653f655ac737d8d7b4705449a 100755
--- a/include/login_form.php
+++ b/include/login_form.php
@@ -6,11 +6,10 @@
 	<link rel="shortcut icon" type="image/png" href="images/favicon.png">
 	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 	<?php
-	foreach (array("lib/dojo/dojo.js",
+	foreach (["lib/dojo/dojo.js",
 				"lib/dojo/tt-rss-layer.js",
 			 	"js/common.js",
-				"js/utility.js",
-				"errors.php?mode=js") as $jsfile) {
+				"js/utility.js"] as $jsfile) {
 
 		echo javascript_tag($jsfile);
 
diff --git a/index.php b/index.php
index 1f2802864f5c55cd9665ab75759525b238df20a2..a21db6fc555b982f10ed1d9008883334029f67c1 100644
--- a/index.php
+++ b/index.php
@@ -69,11 +69,10 @@
 	</script>
 
 	<?php
-	foreach (array("lib/dojo/dojo.js",
+	foreach (["lib/dojo/dojo.js",
 				"lib/dojo/tt-rss-layer.js",
 				"js/tt-rss.js",
-				"js/common.js",
-				"errors.php?mode=js") as $jsfile) {
+				"js/common.js"] as $jsfile) {
 
 		echo javascript_tag($jsfile);
 
diff --git a/js/App.js b/js/App.js
index 68f3740c5845cb3dfe3b8f81b1d1b6f468af0ee7..6cad062f09a3380d554734f7d5c32b4f2adb2b3f 100644
--- a/js/App.js
+++ b/js/App.js
@@ -407,23 +407,15 @@ const App = {
          const counters = reply['counters'];
          const runtime_info = reply['runtime-info'];
 
-         if (error) {
-            const code = error['code'];
-
-            if (code && code != 0) {
-               const msg = error['message'];
-
-               console.warn("[handleRpcJson] received fatal error ", code, msg);
-
-               /* global ERRORS */
-               this.Error.fatal(ERRORS[code], {info: msg, code: code});
-               return false;
-            }
+         if (error && error.code && error.code != App.Error.E_SUCCESS) {
+            console.warn("handleRpcJson: fatal error", error);
+            this.Error.fatal(error.code);
+            return false;
          }
 
          if (seq && this.get_seq() != seq) {
-            console.warn("[handleRpcJson] sequence mismatch: ", seq, '!=', this.get_seq());
-            return;
+            console.warn("handleRpcJson: sequence mismatch: ", seq, '!=', this.get_seq());
+            return false;
          }
 
          // not in preferences
@@ -442,10 +434,13 @@ const App = {
 
          if (netalert) netalert.hide();
 
+         return true;
       } else {
          if (netalert) netalert.show();
 
          Notify.error("Communication problem with server.");
+
+         return false;
 		}
 	},
 	parseRuntimeInfo: function(data) {
@@ -487,20 +482,6 @@ const App = {
 		PluginHost.run(PluginHost.HOOK_RUNTIME_INFO_LOADED, data);
 	},
 	backendSanityCallback: function(reply) {
-		if (!reply) {
-			this.Error.fatal(ERRORS[3]);
-			return;
-		}
-
-		if (reply['error']) {
-			const code = reply['error']['code'];
-
-			if (code && code != 0) {
-				return this.Error.fatal(ERRORS[code],
-					{code: code, info: reply['error']['message']});
-			}
-		}
-
 		console.log("sanity check ok");
 
 		const params = reply['init-params'];
@@ -547,24 +528,25 @@ const App = {
 		this.initSecondStage();
 	},
 	Error: {
+      E_SUCCESS: "E_SUCCESS",
+      E_UNAUTHORIZED: "E_UNAUTHORIZED",
+      E_SCHEMA_MISMATCH: "E_SCHEMA_MISMATCH",
 		fatal: function (error, params = {}) {
-			if (params.code) {
-				if (params.code == 6) {
-					window.location.href = "index.php";
-					return;
-				} else if (params.code == 5) {
-					window.location.href = "public.php?op=dbupdate";
-					return;
-				}
-			}
+         if (error == App.Error.E_UNAUTHORIZED) {
+            window.location.href = "index.php";
+            return;
+         } else if (error == App.Error.E_SCHEMA_MISMATCH) {
+            window.location.href = "public.php?op=dbupdate";
+            return;
+         }
 
-			return this.report(error,
+			return this.report(__("Fatal error: %s").replace("%s", error),
 				{...{title: __("Fatal error")}, ...params});
 		},
 		report: function(error, params = {}) {
 			if (!error) return;
 
-			console.error("[Error.report]", error, params);
+			console.error("error.report:", error, params);
 
 			const message = params.message ? params.message : error.toString();
 
diff --git a/js/common.js b/js/common.js
index df1bf869059062a99982d60d551dd52eae6715af..670ee1b306eae6909800a552299af8e67063c82b 100755
--- a/js/common.js
+++ b/js/common.js
@@ -179,7 +179,10 @@ const xhr = {
 				console.log('xhr.json', '<<<', obj);
 
 				if (obj && typeof App != "undefined")
-					App.handleRpcJson(obj);
+					if (!App.handleRpcJson(obj)) {
+						reject(obj);
+						return;
+					}
 
 				if (complete != undefined) complete(obj);
 
diff --git a/prefs.php b/prefs.php
index 851dd898a63e0bb7c09048240b84310a813e9af8..57ddbba7c4c506543feab223ebe3b95e0911d763 100644
--- a/prefs.php
+++ b/prefs.php
@@ -51,11 +51,10 @@
 	</script>
 
 	<?php
-	foreach (array("lib/dojo/dojo.js",
+	foreach (["lib/dojo/dojo.js",
 				"lib/dojo/tt-rss-layer.js",
 				"js/common.js",
-				"js/prefs.js",
-				"errors.php?mode=js") as $jsfile) {
+				"js/prefs.js"] as $jsfile) {
 
 		echo javascript_tag($jsfile);
 
diff --git a/public.php b/public.php
index 43aa66c1d4a52b6cda2d6b9a6430e37d4845b2fe..28f95d0a9add4fbb6738f06e7804002a3f998dc4 100644
--- a/public.php
+++ b/public.php
@@ -37,7 +37,7 @@
 	if (strpos($method, "_") === 0) {
 		user_error("Refusing to invoke method $method which starts with underscore.", E_USER_WARNING);
 		header("Content-Type: text/json");
-		print error_json(6);
+		print Errors::to_json(Errors::E_UNAUTHORIZED);
 		return;
 	}
 
@@ -50,7 +50,7 @@
 			} else {
 				user_error("Refusing to invoke method $method which has required parameters.", E_USER_WARNING);
 				header("Content-Type: text/json");
-				print error_json(6);
+				print Errors::to_json(Errors::E_UNAUTHORIZED);
 			}
 		} else if (method_exists($handler, 'index')) {
 			$handler->index();
@@ -60,5 +60,5 @@
 	}
 
 	header("Content-Type: text/plain");
-	print error_json(13);
+	print Errors::to_json(Errors::E_UNKNOWN_METHOD);
 ?>