This is an automated email from the ASF dual-hosted git repository.

hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 8f1d2add chore(error): unify error by adding Redis error codes for 
Status (#2362)
8f1d2add is described below

commit 8f1d2add8836b5a70a313df8a4802fdcc1817741
Author: hulk <[email protected]>
AuthorDate: Sat Jun 15 11:43:33 2024 +0800

    chore(error): unify error by adding Redis error codes for Status (#2362)
    
    This PR introduces new error codes to identify Redis error prefixes,
    and forces using status to unify Redis errors. You MUST convert
    it to Status to send a Redis error response.
---
 src/cluster/cluster.cc              |  19 +++----
 src/cluster/cluster_defs.h          |   2 +-
 src/cluster/replication.cc          |   9 ++-
 src/cluster/sync_migrate_context.cc |   2 +-
 src/commands/cmd_bloom_filter.cc    |   8 +--
 src/commands/cmd_cluster.cc         |  20 +++----
 src/commands/cmd_hash.cc            |   9 +--
 src/commands/cmd_key.cc             |  14 ++---
 src/commands/cmd_list.cc            |   6 +-
 src/commands/cmd_replication.cc     |   4 +-
 src/commands/cmd_script.cc          |   3 +-
 src/commands/cmd_server.cc          |  32 +++++------
 src/commands/cmd_stream.cc          |  24 +++-----
 src/commands/cmd_txn.cc             |   3 +-
 src/commands/cmd_zset.cc            |  20 ++-----
 src/commands/error_constants.h      |   6 +-
 src/common/status.h                 |  14 ++++-
 src/server/redis_connection.cc      |  28 ++++-----
 src/server/redis_reply.cc           |  24 +++++++-
 src/server/redis_reply.h            |   6 +-
 src/server/worker.cc                |   5 +-
 src/storage/rdb.cc                  | 109 ++++++++----------------------------
 src/storage/scripting.cc            |  11 ++--
 src/types/json.h                    |   2 +-
 24 files changed, 162 insertions(+), 218 deletions(-)

diff --git a/src/cluster/cluster.cc b/src/cluster/cluster.cc
index d080ed34..8d373e0d 100644
--- a/src/cluster/cluster.cc
+++ b/src/cluster/cluster.cc
@@ -363,7 +363,7 @@ Status Cluster::ImportSlot(redis::Connection *conn, int 
slot, int state) {
 
 Status Cluster::GetClusterInfo(std::string *cluster_infos) {
   if (version_ < 0) {
-    return {Status::ClusterDown, errClusterNoInitialized};
+    return {Status::RedisClusterDown, errClusterNoInitialized};
   }
 
   cluster_infos->clear();
@@ -421,7 +421,7 @@ Status Cluster::GetClusterInfo(std::string *cluster_infos) {
 //          ... continued until done
 Status Cluster::GetSlotsInfo(std::vector<SlotInfo> *slots_infos) {
   if (version_ < 0) {
-    return {Status::ClusterDown, errClusterNoInitialized};
+    return {Status::RedisClusterDown, errClusterNoInitialized};
   }
 
   slots_infos->clear();
@@ -464,7 +464,7 @@ SlotInfo Cluster::genSlotNodeInfo(int start, int end, const 
std::shared_ptr<Clus
 // $version $connected $slot_range
 Status Cluster::GetClusterNodes(std::string *nodes_str) {
   if (version_ < 0) {
-    return {Status::ClusterDown, errClusterNoInitialized};
+    return {Status::RedisClusterDown, errClusterNoInitialized};
   }
 
   *nodes_str = genNodesDescription();
@@ -473,7 +473,7 @@ Status Cluster::GetClusterNodes(std::string *nodes_str) {
 
 StatusOr<std::string> Cluster::GetReplicas(const std::string &node_id) {
   if (version_ < 0) {
-    return {Status::ClusterDown, errClusterNoInitialized};
+    return {Status::RedisClusterDown, errClusterNoInitialized};
   }
 
   auto item = nodes_.find(node_id);
@@ -821,13 +821,13 @@ Status Cluster::CanExecByMySelf(const 
redis::CommandAttributes *attributes, cons
     int cur_slot = GetSlotIdFromKey(cmd_tokens[i]);
     if (slot == -1) slot = cur_slot;
     if (slot != cur_slot) {
-      return {Status::RedisExecErr, "CROSSSLOT Attempted to access keys that 
don't hash to the same slot"};
+      return {Status::RedisCrossSlot, "Attempted to access keys that don't 
hash to the same slot"};
     }
   }
   if (slot == -1) return Status::OK();
 
   if (slots_nodes_[slot] == nullptr) {
-    return {Status::ClusterDown, "CLUSTERDOWN Hash slot not served"};
+    return {Status::RedisClusterDown, "Hash slot not served"};
   }
 
   if (myself_ && myself_ == slots_nodes_[slot]) {
@@ -835,12 +835,12 @@ Status Cluster::CanExecByMySelf(const 
redis::CommandAttributes *attributes, cons
     // Server can't change the topology directly, so we record the migrated 
slots
     // to move the requests of the migrated slots to the destination node.
     if (migrated_slots_.count(slot) > 0) {  // I'm not serving the migrated 
slot
-      return {Status::RedisExecErr, fmt::format("MOVED {} {}", slot, 
migrated_slots_[slot])};
+      return {Status::RedisMoved, fmt::format("{} {}", slot, 
migrated_slots_[slot])};
     }
     // To keep data consistency, slot will be forbidden write while sending 
the last incremental data.
     // During this phase, the requests of the migrating slot has to be 
rejected.
     if ((attributes->flags & redis::kCmdWrite) && IsWriteForbiddenSlot(slot)) {
-      return {Status::RedisExecErr, "TRYAGAIN Can't write to slot being 
migrated which is in write forbidden phase"};
+      return {Status::RedisTryAgain, "Can't write to slot being migrated which 
is in write forbidden phase"};
     }
 
     return Status::OK();  // I'm serving this slot
@@ -868,8 +868,7 @@ Status Cluster::CanExecByMySelf(const 
redis::CommandAttributes *attributes, cons
     return Status::OK();  // My master is serving this slot
   }
 
-  return {Status::RedisExecErr,
-          fmt::format("MOVED {} {}:{}", slot, slots_nodes_[slot]->host, 
slots_nodes_[slot]->port)};
+  return {Status::RedisMoved, fmt::format("{} {}:{}", slot, 
slots_nodes_[slot]->host, slots_nodes_[slot]->port)};
 }
 
 // Only HARD mode is meaningful to the Kvrocks cluster,
diff --git a/src/cluster/cluster_defs.h b/src/cluster/cluster_defs.h
index 6638db88..0178ac75 100644
--- a/src/cluster/cluster_defs.h
+++ b/src/cluster/cluster_defs.h
@@ -36,7 +36,7 @@ inline constexpr const char *errSlotOutOfRange = "Slot is out 
of range";
 inline constexpr const char *errInvalidClusterVersion = "Invalid cluster 
version";
 inline constexpr const char *errSlotOverlapped = "Slot distribution is 
overlapped";
 inline constexpr const char *errNoMasterNode = "The node isn't a master";
-inline constexpr const char *errClusterNoInitialized = "CLUSTERDOWN The 
cluster is not initialized";
+inline constexpr const char *errClusterNoInitialized = "The cluster is not 
initialized";
 inline constexpr const char *errInvalidClusterNodeInfo = "Invalid cluster 
nodes info";
 inline constexpr const char *errInvalidImportState = "Invalid import state";
 
diff --git a/src/cluster/replication.cc b/src/cluster/replication.cc
index a3567284..3de51a94 100644
--- a/src/cluster/replication.cc
+++ b/src/cluster/replication.cc
@@ -1000,15 +1000,18 @@ Status ReplicationThread::parseWriteBatch(const 
std::string &batch_string) {
 }
 
 bool ReplicationThread::isRestoringError(std::string_view err) {
-  return err == std::string(RESP_PREFIX_ERROR) + redis::errRestoringBackup;
+  // err doesn't contain the CRLF, so cannot use redis::Error here.
+  return err == RESP_PREFIX_ERROR + 
redis::StatusToRedisErrorMsg({Status::RedisLoading, redis::errRestoringBackup});
 }
 
 bool ReplicationThread::isWrongPsyncNum(std::string_view err) {
-  return err == std::string(RESP_PREFIX_ERROR) + redis::errWrongNumArguments;
+  // err doesn't contain the CRLF, so cannot use redis::Error here.
+  return err == RESP_PREFIX_ERROR + 
redis::StatusToRedisErrorMsg({Status::NotOK, redis::errWrongNumArguments});
 }
 
 bool ReplicationThread::isUnknownOption(std::string_view err) {
-  return err == fmt::format("{}ERR {}", RESP_PREFIX_ERROR, 
redis::errUnknownOption);
+  // err doesn't contain the CRLF, so cannot use redis::Error here.
+  return err == RESP_PREFIX_ERROR + 
redis::StatusToRedisErrorMsg({Status::NotOK, redis::errUnknownOption});
 }
 
 rocksdb::Status WriteBatchHandler::PutCF(uint32_t column_family_id, const 
rocksdb::Slice &key,
diff --git a/src/cluster/sync_migrate_context.cc 
b/src/cluster/sync_migrate_context.cc
index 3ba2806c..f1de89b6 100644
--- a/src/cluster/sync_migrate_context.cc
+++ b/src/cluster/sync_migrate_context.cc
@@ -68,7 +68,7 @@ void SyncMigrateContext::OnWrite(bufferevent *bev) {
   if (migrate_result_) {
     conn_->Reply(redis::SimpleString("OK"));
   } else {
-    conn_->Reply(redis::Error("ERR " + migrate_result_.Msg()));
+    conn_->Reply(redis::Error(migrate_result_));
   }
 
   timer_.reset();
diff --git a/src/commands/cmd_bloom_filter.cc b/src/commands/cmd_bloom_filter.cc
index f33979e3..bb2df94e 100644
--- a/src/commands/cmd_bloom_filter.cc
+++ b/src/commands/cmd_bloom_filter.cc
@@ -33,7 +33,7 @@ constexpr const char *errInvalidErrorRate = "error rate 
should be between 0 and
 constexpr const char *errInvalidCapacity = "capacity should be larger than 0";
 constexpr const char *errInvalidExpansion = "expansion should be greater or 
equal to 1";
 constexpr const char *errNonscalingButExpand = "nonscaling filters cannot 
expand";
-constexpr const char *errFilterFull = "ERR nonscaling filter is full";
+constexpr const char *errFilterFull = "nonscaling filter is full";
 }  // namespace
 
 namespace redis {
@@ -119,7 +119,7 @@ class CommandBFAdd : public Commander {
         *output = redis::Integer(0);
         break;
       case BloomFilterAddResult::kFull:
-        *output = redis::Error(errFilterFull);
+        *output = redis::Error({Status::NotOK, errFilterFull});
         break;
     }
     return Status::OK();
@@ -152,7 +152,7 @@ class CommandBFMAdd : public Commander {
           *output += redis::Integer(0);
           break;
         case BloomFilterAddResult::kFull:
-          *output += redis::Error(errFilterFull);
+          *output += redis::Error({Status::NotOK, errFilterFull});
           break;
       }
     }
@@ -248,7 +248,7 @@ class CommandBFInsert : public Commander {
           *output += redis::Integer(0);
           break;
         case BloomFilterAddResult::kFull:
-          *output += redis::Error(errFilterFull);
+          *output += redis::Error({Status::NotOK, errFilterFull});
           break;
       }
     }
diff --git a/src/commands/cmd_cluster.cc b/src/commands/cmd_cluster.cc
index 8ada4e8e..0f9b1760 100644
--- a/src/commands/cmd_cluster.cc
+++ b/src/commands/cmd_cluster.cc
@@ -90,7 +90,7 @@ class CommandCluster : public Commander {
           }
         }
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "nodes") {
       std::string nodes_desc;
@@ -98,7 +98,7 @@ class CommandCluster : public Commander {
       if (s.IsOK()) {
         *output = conn->VerbatimString("txt", nodes_desc);
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "info") {
       std::string cluster_info;
@@ -106,21 +106,21 @@ class CommandCluster : public Commander {
       if (s.IsOK()) {
         *output = conn->VerbatimString("txt", cluster_info);
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "import") {
       Status s = srv->cluster->ImportSlot(conn, static_cast<int>(slot_), 
state_);
       if (s.IsOK()) {
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "reset") {
       Status s = srv->cluster->Reset();
       if (s.IsOK()) {
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "replicas") {
       auto node_id = args_[2];
@@ -128,7 +128,7 @@ class CommandCluster : public Commander {
       if (s.IsOK()) {
         *output = conn->VerbatimString("txt", s.GetValue());
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else {
       return {Status::RedisExecErr, "Invalid cluster command options"};
@@ -252,7 +252,7 @@ class CommandClusterX : public Commander {
         need_persist_nodes_info = true;
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "setnodeid") {
       Status s = srv->cluster->SetNodeId(args_[2]);
@@ -260,7 +260,7 @@ class CommandClusterX : public Commander {
         need_persist_nodes_info = true;
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "setslot") {
       Status s = srv->cluster->SetSlotRanges(slot_ranges_, args_[4], 
set_version_);
@@ -268,7 +268,7 @@ class CommandClusterX : public Commander {
         need_persist_nodes_info = true;
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else if (subcommand_ == "version") {
       int64_t v = srv->cluster->GetVersion();
@@ -287,7 +287,7 @@ class CommandClusterX : public Commander {
         }
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else {
       return {Status::RedisExecErr, "Invalid cluster command options"};
diff --git a/src/commands/cmd_hash.cc b/src/commands/cmd_hash.cc
index 6db97f89..677f131e 100644
--- a/src/commands/cmd_hash.cc
+++ b/src/commands/cmd_hash.cc
@@ -326,16 +326,11 @@ class CommandHRangeByLex : public Commander {
         return parser.InvalidSyntax();
       }
     }
-    Status s;
     if (spec_.reversed) {
-      s = ParseRangeLexSpec(args[3], args[2], &spec_);
+      return ParseRangeLexSpec(args[3], args[2], &spec_);
     } else {
-      s = ParseRangeLexSpec(args[2], args[3], &spec_);
+      return ParseRangeLexSpec(args[2], args[3], &spec_);
     }
-    if (!s.IsOK()) {
-      return {Status::RedisParseErr, s.Msg()};
-    }
-    return Status::OK();
   }
 
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
diff --git a/src/commands/cmd_key.cc b/src/commands/cmd_key.cc
index 24d8fe29..d7219841 100644
--- a/src/commands/cmd_key.cc
+++ b/src/commands/cmd_key.cc
@@ -480,8 +480,7 @@ class CommandSort : public Commander {
     }
 
     if (type != RedisType::kRedisList && type != RedisType::kRedisSet && type 
!= RedisType::kRedisZSet) {
-      *output = Error("WRONGTYPE Operation against a key holding the wrong 
kind of value");
-      return Status::OK();
+      return {Status::RedisWrongType, "Operation against a key holding the 
wrong kind of value"};
     }
 
     /* When sorting a set with no sort specified, we must sort the output
@@ -508,15 +507,12 @@ class CommandSort : public Commander {
 
     switch (res) {
       case Database::SortResult::UNKNOWN_TYPE:
-        *output = redis::Error("Unknown Type");
-        break;
+        return {Status::RedisErrorNoPrefix, "Unknown Type"};
       case Database::SortResult::DOUBLE_CONVERT_ERROR:
-        *output = redis::Error("One or more scores can't be converted into 
double");
-        break;
+        return {Status::RedisErrorNoPrefix, "One or more scores can't be 
converted into double"};
       case Database::SortResult::LIMIT_EXCEEDED:
-        *output = redis::Error("The number of elements to be sorted exceeds 
SORT_LENGTH_LIMIT = " +
-                               std::to_string(SORT_LENGTH_LIMIT));
-        break;
+        return {Status::RedisErrorNoPrefix,
+                "The number of elements to be sorted exceeds SORT_LENGTH_LIMIT 
= " + std::to_string(SORT_LENGTH_LIMIT)};
       case Database::SortResult::DONE:
         if (sort_argument_.storekey.empty()) {
           std::vector<std::string> output_vec;
diff --git a/src/commands/cmd_list.cc b/src/commands/cmd_list.cc
index f354d64c..e9e17266 100644
--- a/src/commands/cmd_list.cc
+++ b/src/commands/cmd_list.cc
@@ -304,7 +304,7 @@ class CommandBPop : public BlockingCommander {
         conn_->Reply(conn_->MultiBulkString({*last_key_ptr, std::move(elem)}));
       }
     } else if (!s.IsNotFound()) {
-      conn_->Reply(redis::Error("ERR " + s.ToString()));
+      conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
     }
 
     return s;
@@ -414,7 +414,7 @@ class CommandBLMPop : public BlockingCommander {
         conn_->Reply(redis::Array({redis::BulkString(chosen_key), 
std::move(elems_bulk)}));
       }
     } else if (!s.IsNotFound()) {
-      conn_->Reply(redis::Error("ERR " + s.ToString()));
+      conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
     }
 
     return s;
@@ -757,7 +757,7 @@ class CommandBLMove : public BlockingCommander {
     std::string elem;
     auto s = list_db.LMove(args_[1], args_[2], src_left_, dst_left_, &elem);
     if (!s.ok() && !s.IsNotFound()) {
-      conn_->Reply(redis::Error("ERR " + s.ToString()));
+      conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
       return true;
     }
 
diff --git a/src/commands/cmd_replication.cc b/src/commands/cmd_replication.cc
index 5ec6faa0..d3f3c0f2 100644
--- a/src/commands/cmd_replication.cc
+++ b/src/commands/cmd_replication.cc
@@ -102,7 +102,7 @@ class CommandPSync : public Commander {
     srv->stats.IncrPSyncOKCount();
     s = srv->AddSlave(conn, next_repl_seq_);
     if (!s.IsOK()) {
-      std::string err = redis::Error(s.Msg());
+      std::string err = redis::Error(s);
       s = util::SockSend(conn->GetFD(), err, conn->GetBufferEvent());
       if (!s.IsOK()) {
         LOG(WARNING) << "failed to send error message to the replica: " << 
s.Msg();
@@ -230,7 +230,7 @@ class CommandFetchMeta : public Commander {
       std::string files;
       auto s = 
engine::Storage::ReplDataManager::GetFullReplDataInfo(srv->storage, &files);
       if (!s.IsOK()) {
-        s = util::SockSend(repl_fd, redis::Error("can't create db 
checkpoint"), bev);
+        s = util::SockSend(repl_fd, redis::Error({Status::RedisErrorNoPrefix, 
"can't create db checkpoint"}), bev);
         if (!s.IsOK()) {
           LOG(WARNING) << "[replication] Failed to send error response: " << 
s.Msg();
         }
diff --git a/src/commands/cmd_script.cc b/src/commands/cmd_script.cc
index 34a71837..2547c289 100644
--- a/src/commands/cmd_script.cc
+++ b/src/commands/cmd_script.cc
@@ -31,8 +31,7 @@ class CommandEvalImpl : public Commander {
  public:
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     if (evalsha && args_[1].size() != 40) {
-      *output = redis::Error(errNoMatchingScript);
-      return Status::OK();
+      return {Status::RedisNoScript, errNoMatchingScript};
     }
 
     int64_t numkeys = GET_OR_RET(ParseInt<int64_t>(args_[2], 10));
diff --git a/src/commands/cmd_server.cc b/src/commands/cmd_server.cc
index 114eee15..d595875d 100644
--- a/src/commands/cmd_server.cc
+++ b/src/commands/cmd_server.cc
@@ -95,17 +95,17 @@ class CommandNamespace : public Commander {
       }
     } else if (args_.size() == 4 && sub_command == "set") {
       Status s = srv->GetNamespace()->Set(args_[2], args_[3]);
-      *output = s.IsOK() ? redis::SimpleString("OK") : redis::Error("ERR " + 
s.Msg());
+      *output = s.IsOK() ? redis::SimpleString("OK") : redis::Error(s);
       LOG(WARNING) << "Updated namespace: " << args_[2] << " with token: " << 
args_[3] << ", addr: " << conn->GetAddr()
                    << ", result: " << s.Msg();
     } else if (args_.size() == 4 && sub_command == "add") {
       Status s = srv->GetNamespace()->Add(args_[2], args_[3]);
-      *output = s.IsOK() ? redis::SimpleString("OK") : redis::Error("ERR " + 
s.Msg());
+      *output = s.IsOK() ? redis::SimpleString("OK") : redis::Error(s);
       LOG(WARNING) << "New namespace: " << args_[2] << " with token: " << 
args_[3] << ", addr: " << conn->GetAddr()
                    << ", result: " << s.Msg();
     } else if (args_.size() == 3 && sub_command == "del") {
       Status s = srv->GetNamespace()->Del(args_[2]);
-      *output = s.IsOK() ? redis::SimpleString("OK") : redis::Error("ERR " + 
s.Msg());
+      *output = s.IsOK() ? redis::SimpleString("OK") : redis::Error(s);
       LOG(WARNING) << "Deleted namespace: " << args_[2] << ", addr: " << 
conn->GetAddr() << ", result: " << s.Msg();
     } else {
       return {Status::RedisExecErr, "NAMESPACE subcommand must be one of GET, 
SET, DEL, ADD"};
@@ -224,7 +224,7 @@ class CommandConfig : public Commander {
 
     if (args_.size() == 2 && sub_command == "rewrite") {
       Status s = config->Rewrite(srv->GetNamespace()->List());
-      if (!s.IsOK()) return {Status::RedisExecErr, s.Msg()};
+      if (!s.IsOK()) return s;
 
       *output = redis::SimpleString("OK");
       LOG(INFO) << "# CONFIG REWRITE executed with success";
@@ -315,7 +315,7 @@ class CommandDBSize : public Commander {
       if (s.IsOK()) {
         *output = redis::SimpleString("OK");
       } else {
-        return {Status::RedisExecErr, s.Msg()};
+        return s;
       }
     } else {
       return {Status::RedisExecErr, "DBSIZE subcommand only supports scan"};
@@ -630,9 +630,9 @@ class CommandDebug : public Commander {
       } else if (protocol_type_ == "verbatim") {  // verbatim string
         *output = conn->VerbatimString("txt", "verbatim string");
       } else {
-        *output = redis::Error(
-            "Wrong protocol type name. Please use one of the following: "
-            
"string|integer|double|array|set|bignum|true|false|null|attrib|verbatim");
+        return {Status::RedisErrorNoPrefix,
+                "Wrong protocol type name. Please use one of the following: "
+                
"string|integer|double|array|set|bignum|true|false|null|attrib|verbatim"};
       }
     } else if (subcommand_ == "dbsize-limit") {
       srv->storage->SetDBSizeLimit(dbsize_limit_);
@@ -741,8 +741,7 @@ class CommandHello final : public Commander {
       // kvrocks only supports REPL2 by now, but for supporting some
       // `hello 3`, it will not report error when using 3.
       if (protocol < 2 || protocol > 3) {
-        conn->Reply(redis::Error("NOPROTO unsupported protocol version"));
-        return Status::OK();
+        return {Status::RedisNoProto, "unsupported protocol version"};
       }
     }
 
@@ -986,9 +985,7 @@ class CommandSlaveOf : public Commander {
     }
 
     auto s = IsTryingToReplicateItself(srv, host_, port_);
-    if (!s.IsOK()) {
-      return {Status::RedisExecErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
     s = srv->AddMaster(host_, port_, false);
     if (s.IsOK()) {
       *output = redis::SimpleString("OK");
@@ -1101,7 +1098,7 @@ class CommandRestore : public Commander {
     auto stream_ptr = std::make_unique<RdbStringStream>(args_[3]);
     RDB rdb(srv->storage, conn->GetNamespace(), std::move(stream_ptr));
     auto s = rdb.Restore(args_[1], args_[3], ttl_ms_);
-    if (!s.IsOK()) return {Status::RedisExecErr, s.Msg()};
+    if (!s.IsOK()) return s;
     *output = redis::SimpleString("OK");
     return Status::OK();
   }
@@ -1215,9 +1212,8 @@ class CommandApplyBatch : public Commander {
     auto options = svr->storage->DefaultWriteOptions();
     options.low_pri = low_pri_;
     auto s = svr->storage->ApplyWriteBatch(options, std::move(raw_batch_));
-    if (!s.IsOK()) {
-      return {Status::RedisExecErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
+
     *output = redis::Integer(size);
     return Status::OK();
   }
@@ -1262,7 +1258,7 @@ class CommandDump : public Commander {
     auto stream_ptr = std::make_unique<RdbStringStream>(result);
     RDB rdb(srv->storage, conn->GetNamespace(), std::move(stream_ptr));
     auto s = rdb.Dump(key, type);
-    if (!s.IsOK()) return {Status::RedisExecErr, s.Msg()};
+    if (!s.IsOK()) return s;
     CHECK(dynamic_cast<RdbStringStream *>(rdb.GetStream().get()) != nullptr);
     *output = redis::BulkString(static_cast<RdbStringStream 
*>(rdb.GetStream().get())->GetInput());
     return Status::OK();
diff --git a/src/commands/cmd_stream.cc b/src/commands/cmd_stream.cc
index 7b3bf54e..ba4f7d98 100644
--- a/src/commands/cmd_stream.cc
+++ b/src/commands/cmd_stream.cc
@@ -39,9 +39,7 @@ class CommandXAck : public Commander {
     StreamEntryID tmp_id;
     for (size_t i = 3; i < args.size(); ++i) {
       auto s = ParseStreamEntryID(args[i], &tmp_id);
-      if (!s.IsOK()) {
-        return {Status::RedisParseErr, s.Msg()};
-      }
+      if (!s.IsOK()) return s;
       entry_ids_.emplace_back(tmp_id);
     }
 
@@ -130,9 +128,7 @@ class CommandXAdd : public Commander {
         }
 
         auto s = ParseStreamEntryID(args[min_id_idx], &min_id_);
-        if (!s.IsOK()) {
-          return {Status::RedisParseErr, s.Msg()};
-        }
+        if (!s.IsOK()) return s;
 
         with_min_id_ = true;
         i += eq_sign_found ? 3 : 2;
@@ -145,9 +141,7 @@ class CommandXAdd : public Commander {
 
       if (!entry_id_found) {
         auto result = ParseNextStreamEntryIDStrategy(val);
-        if (!result.IsOK()) {
-          return {Status::RedisParseErr, result.Msg()};
-        }
+        if (!result.IsOK()) return result;
 
         next_id_strategy_ = std::move(*result);
 
@@ -1130,7 +1124,7 @@ class CommandXRead : public Commander,
       std::vector<StreamEntry> result;
       auto s = stream_db.Range(streams_[i], options, &result);
       if (!s.ok() && !s.IsNotFound()) {
-        conn_->Reply(redis::Error("ERR " + s.ToString()));
+        conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
         return;
       }
 
@@ -1423,7 +1417,7 @@ class CommandXReadGroup : public Commander,
       auto s = stream_db.RangeWithPending(streams_[i], options, &result, 
group_name_, consumer_name_, noack_,
                                           latest_marks_[i]);
       if (!s.ok() && !s.IsNotFound()) {
-        conn_->Reply(redis::Error("ERR " + s.ToString()));
+        conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
         return;
       }
 
@@ -1599,9 +1593,7 @@ class CommandXSetId : public Commander {
     stream_name_ = args[1];
 
     auto s = redis::ParseStreamEntryID(args[2], &last_id_);
-    if (!s.IsOK()) {
-      return {Status::RedisParseErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
 
     if (args.size() == 3) {
       return Status::OK();
@@ -1619,9 +1611,7 @@ class CommandXSetId : public Commander {
       } else if (util::EqualICase(args[i], "maxdeletedid") && i + 1 < 
args.size()) {
         StreamEntryID id;
         s = redis::ParseStreamEntryID(args[i + 1], &id);
-        if (!s.IsOK()) {
-          return {Status::RedisParseErr, s.Msg()};
-        }
+        if (!s.IsOK()) return s;
 
         max_deleted_id_ = std::make_optional<StreamEntryID>(id.ms, id.seq);
         i += 2;
diff --git a/src/commands/cmd_txn.cc b/src/commands/cmd_txn.cc
index fa1a47aa..130533fb 100644
--- a/src/commands/cmd_txn.cc
+++ b/src/commands/cmd_txn.cc
@@ -68,8 +68,7 @@ class CommandExec : public Commander {
     auto reset_multiexec = MakeScopeExit([conn] { conn->ResetMultiExec(); });
 
     if (conn->IsMultiError()) {
-      *output = redis::Error("EXECABORT Transaction discarded");
-      return Status::OK();
+      return {Status::RedisExecAbort, "Transaction discarded"};
     }
 
     if (srv->IsWatchedKeysModified(conn)) {
diff --git a/src/commands/cmd_zset.cc b/src/commands/cmd_zset.cc
index c32c976a..715747ab 100644
--- a/src/commands/cmd_zset.cc
+++ b/src/commands/cmd_zset.cc
@@ -136,9 +136,7 @@ class CommandZCount : public Commander {
  public:
   Status Parse(const std::vector<std::string> &args) override {
     Status s = ParseRangeScoreSpec(args[2], args[3], &spec_);
-    if (!s.IsOK()) {
-      return {Status::RedisParseErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
     return Commander::Parse(args);
   }
 
@@ -204,9 +202,7 @@ class CommandZLexCount : public Commander {
  public:
   Status Parse(const std::vector<std::string> &args) override {
     Status s = ParseRangeLexSpec(args[2], args[3], &spec_);
-    if (!s.IsOK()) {
-      return {Status::RedisParseErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
 
     return Commander::Parse(args);
   }
@@ -369,7 +365,7 @@ class CommandBZPop : public BlockingCommander {
     redis::ZSet zset_db(srv_->storage, conn_->GetNamespace());
     auto s = PopFromMultipleZsets(&zset_db, keys_, min_, 1, &user_key, 
&member_scores);
     if (!s.ok()) {
-      conn_->Reply(redis::Error("ERR " + s.ToString()));
+      conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
       return true;
     }
 
@@ -548,7 +544,7 @@ class CommandBZMPop : public BlockingCommander {
     redis::ZSet zset_db(srv_->storage, conn_->GetNamespace());
     auto s = PopFromMultipleZsets(&zset_db, keys_, flag_ == ZSET_MIN, count_, 
&user_key, &member_scores);
     if (!s.ok()) {
-      conn_->Reply(redis::Error("ERR " + s.ToString()));
+      conn_->Reply(redis::Error({Status::NotOK, s.ToString()}));
       return true;
     }
 
@@ -985,9 +981,7 @@ class CommandZRemRangeByScore : public Commander {
  public:
   Status Parse(const std::vector<std::string> &args) override {
     Status s = ParseRangeScoreSpec(args[2], args[3], &spec_);
-    if (!s.IsOK()) {
-      return {Status::RedisParseErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
     return Commander::Parse(args);
   }
 
@@ -1014,9 +1008,7 @@ class CommandZRemRangeByLex : public Commander {
  public:
   Status Parse(const std::vector<std::string> &args) override {
     Status s = ParseRangeLexSpec(args[2], args[3], &spec_);
-    if (!s.IsOK()) {
-      return {Status::RedisParseErr, s.Msg()};
-    }
+    if (!s.IsOK()) return s;
     return Commander::Parse(args);
   }
 
diff --git a/src/commands/error_constants.h b/src/commands/error_constants.h
index 2074a705..ea2c38b7 100644
--- a/src/commands/error_constants.h
+++ b/src/commands/error_constants.h
@@ -40,10 +40,10 @@ inline constexpr const char *errLimitOptionNotAllowed =
 inline constexpr const char *errZSetLTGTNX = "GT, LT, and/or NX options at the 
same time are not compatible";
 inline constexpr const char *errScoreIsNotValidFloat = "score is not a valid 
float";
 inline constexpr const char *errValueIsNotFloat = "value is not a valid float";
-inline constexpr const char *errNoMatchingScript = "NOSCRIPT No matching 
script. Please use EVAL";
+inline constexpr const char *errNoMatchingScript = "No matching script. Please 
use EVAL";
 inline constexpr const char *errUnknownOption = "unknown option";
 inline constexpr const char *errUnknownSubcommandOrWrongArguments = "Unknown 
subcommand or wrong number of arguments";
-inline constexpr const char *errWrongNumArguments = "ERR wrong number of 
arguments";
-inline constexpr const char *errRestoringBackup = "LOADING kvrocks is 
restoring the db from backup";
+inline constexpr const char *errWrongNumArguments = "wrong number of 
arguments";
+inline constexpr const char *errRestoringBackup = "kvrocks is restoring the db 
from backup";
 
 }  // namespace redis
diff --git a/src/common/status.h b/src/common/status.h
index b425ea5b..d06c2f06 100644
--- a/src/common/status.h
+++ b/src/common/status.h
@@ -50,9 +50,21 @@ class [[nodiscard]] Status {
     RedisInvalidCmd,
     RedisParseErr,
     RedisExecErr,
+    RedisErrorNoPrefix,
+    RedisNoProto,
+    RedisLoading,
+    RedisMasterDown,
+    RedisNoScript,
+    RedisNoAuth,
+    RedisWrongType,
+    RedisReadOnly,
+    RedisExecAbort,
+    RedisMoved,
+    RedisCrossSlot,
+    RedisTryAgain,
+    RedisClusterDown,
 
     // Cluster
-    ClusterDown,
     ClusterInvalidInfo,
 
     // Blocking
diff --git a/src/server/redis_connection.cc b/src/server/redis_connection.cc
index f3960a66..0457c71d 100644
--- a/src/server/redis_connection.cc
+++ b/src/server/redis_connection.cc
@@ -87,7 +87,7 @@ void Connection::OnRead(struct bufferevent *bev) {
   auto s = req_.Tokenize(Input());
   if (!s.IsOK()) {
     EnableFlag(redis::Connection::kCloseAfterReply);
-    Reply(redis::Error("ERR " + s.Msg()));
+    Reply(redis::Error(s));
     LOG(INFO) << "[connection] Failed to tokenize the request. Error: " << 
s.Msg();
     return;
   }
@@ -432,7 +432,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> 
*to_process_cmds) {
     auto cmd_s = Server::LookupAndCreateCommand(cmd_tokens.front());
     if (!cmd_s.IsOK()) {
       if (is_multi_exec) multi_error_ = true;
-      Reply(redis::Error("ERR unknown command " + cmd_tokens.front()));
+      Reply(redis::Error({Status::NotOK, "unknown command " + 
cmd_tokens.front()}));
       continue;
     }
     auto current_cmd = std::move(*cmd_s);
@@ -444,7 +444,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> 
*to_process_cmds) {
     if (GetNamespace().empty()) {
       if (!password.empty()) {
         if (cmd_name != "auth" && cmd_name != "hello") {
-          Reply(redis::Error("NOAUTH Authentication required."));
+          Reply(redis::Error({Status::RedisNoAuth, "Authentication 
required."}));
           continue;
         }
       } else {
@@ -477,7 +477,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> 
*to_process_cmds) {
     }
 
     if (srv_->IsLoading() && !(cmd_flags & kCmdLoading)) {
-      Reply(redis::Error(errRestoringBackup));
+      Reply(redis::Error({Status::RedisLoading, errRestoringBackup}));
       if (is_multi_exec) multi_error_ = true;
       continue;
     }
@@ -485,7 +485,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> 
*to_process_cmds) {
     int tokens = static_cast<int>(cmd_tokens.size());
     if (!attributes->CheckArity(tokens)) {
       if (is_multi_exec) multi_error_ = true;
-      Reply(redis::Error("ERR wrong number of arguments"));
+      Reply(redis::Error({Status::NotOK, "wrong number of arguments"}));
       continue;
     }
 
@@ -493,12 +493,12 @@ void 
Connection::ExecuteCommands(std::deque<CommandTokens> *to_process_cmds) {
     auto s = current_cmd->Parse();
     if (!s.IsOK()) {
       if (is_multi_exec) multi_error_ = true;
-      Reply(redis::Error("ERR " + s.Msg()));
+      Reply(redis::Error(s));
       continue;
     }
 
     if (is_multi_exec && (cmd_flags & kCmdNoMulti)) {
-      Reply(redis::Error("ERR Can't execute " + cmd_name + " in MULTI"));
+      Reply(redis::Error({Status::NotOK, "Can't execute " + cmd_name + " in 
MULTI"}));
       multi_error_ = true;
       continue;
     }
@@ -507,7 +507,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> 
*to_process_cmds) {
       s = srv_->cluster->CanExecByMySelf(attributes, cmd_tokens, this);
       if (!s.IsOK()) {
         if (is_multi_exec) multi_error_ = true;
-        Reply(redis::Error(s.Msg()));
+        Reply(redis::Error(s));
         continue;
       }
     }
@@ -525,20 +525,20 @@ void 
Connection::ExecuteCommands(std::deque<CommandTokens> *to_process_cmds) {
     }
 
     if (config->slave_readonly && srv_->IsSlave() && (cmd_flags & kCmdWrite)) {
-      Reply(redis::Error("READONLY You can't write against a read only 
slave."));
+      Reply(redis::Error({Status::RedisReadOnly, "You can't write against a 
read only slave."}));
       continue;
     }
 
     if ((cmd_flags & kCmdWrite) && !(cmd_flags & kCmdNoDBSizeCheck) && 
srv_->storage->ReachedDBSizeLimit()) {
-      Reply(redis::Error("ERR write command not allowed when reached 
max-db-size."));
+      Reply(redis::Error({Status::NotOK, "write command not allowed when 
reached max-db-size."}));
       continue;
     }
 
     if (!config->slave_serve_stale_data && srv_->IsSlave() && cmd_name != 
"info" && cmd_name != "slaveof" &&
         srv_->GetReplicationState() != kReplConnected) {
-      Reply(
-          redis::Error("MASTERDOWN Link with MASTER is down "
-                       "and slave-serve-stale-data is set to 'no'."));
+      Reply(redis::Error({Status::RedisMasterDown,
+                          "Link with MASTER is down "
+                          "and slave-serve-stale-data is set to 'no'."}));
       continue;
     }
 
@@ -585,7 +585,7 @@ void Connection::ExecuteCommands(std::deque<CommandTokens> 
*to_process_cmds) {
 
     // Reply for MULTI
     if (!s.IsOK()) {
-      Reply(redis::Error("ERR " + s.Msg()));
+      Reply(redis::Error(s));
       continue;
     }
 
diff --git a/src/server/redis_reply.cc b/src/server/redis_reply.cc
index d2143b6a..20e15e51 100644
--- a/src/server/redis_reply.cc
+++ b/src/server/redis_reply.cc
@@ -20,15 +20,37 @@
 
 #include "redis_reply.h"
 
+#include <map>
 #include <numeric>
 
+const std::map<Status::Code, std::string> redisErrorPrefixMapping = {
+    {Status::RedisErrorNoPrefix, ""},         {Status::RedisNoProto, 
"NOPROTO"},
+    {Status::RedisLoading, "LOADING"},        {Status::RedisMasterDown, 
"MASTERDOWN"},
+    {Status::RedisNoScript, "NOSCRIPT"},      {Status::RedisNoAuth, "NOAUTH"},
+    {Status::RedisWrongType, "WRONGTYPE"},    {Status::RedisReadOnly, 
"READONLY"},
+    {Status::RedisExecAbort, "EXECABORT"},    {Status::RedisMoved, "MOVED"},
+    {Status::RedisCrossSlot, "CROSSSLOT"},    {Status::RedisTryAgain, 
"TRYAGAIN"},
+    {Status::RedisClusterDown, "CLUSTERDOWN"}};
+
 namespace redis {
 
 void Reply(evbuffer *output, const std::string &data) { evbuffer_add(output, 
data.c_str(), data.length()); }
 
 std::string SimpleString(const std::string &data) { return "+" + data + CRLF; }
 
-std::string Error(const std::string &err) { return "-" + err + CRLF; }
+std::string Error(const Status &s) { return RESP_PREFIX_ERROR + 
StatusToRedisErrorMsg(s) + CRLF; }
+
+std::string StatusToRedisErrorMsg(const Status &s) {
+  CHECK(!s.IsOK());
+  std::string prefix = "ERR";
+  if (auto it = redisErrorPrefixMapping.find(s.GetCode()); it != 
redisErrorPrefixMapping.end()) {
+    prefix = it->second;
+  }
+  if (prefix.empty()) {
+    return s.Msg();
+  }
+  return prefix + " " + s.Msg();
+}
 
 std::string BulkString(const std::string &data) { return "$" + 
std::to_string(data.length()) + CRLF + data + CRLF; }
 
diff --git a/src/server/redis_reply.h b/src/server/redis_reply.h
index c7fd3b2a..f50c559d 100644
--- a/src/server/redis_reply.h
+++ b/src/server/redis_reply.h
@@ -25,6 +25,8 @@
 #include <string>
 #include <vector>
 
+#include "status.h"
+
 #define CRLF "\r\n"                    // NOLINT
 #define RESP_PREFIX_ERROR "-"          // NOLINT
 #define RESP_PREFIX_SIMPLE_STRING "+"  // NOLINT
@@ -35,7 +37,9 @@ enum class RESP { v2, v3 };
 
 void Reply(evbuffer *output, const std::string &data);
 std::string SimpleString(const std::string &data);
-std::string Error(const std::string &err);
+
+std::string Error(const Status &s);
+std::string StatusToRedisErrorMsg(const Status &s);
 
 template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
 std::string Integer(T data) {
diff --git a/src/server/worker.cc b/src/server/worker.cc
index 1e8fed37..22054e1f 100644
--- a/src/server/worker.cc
+++ b/src/server/worker.cc
@@ -167,7 +167,7 @@ void Worker::newTCPConnection(evconnlistener *listener, 
evutil_socket_t fd, sock
 
   s = AddConnection(conn);
   if (!s.IsOK()) {
-    std::string err_msg = redis::Error("ERR " + s.Msg());
+    std::string err_msg = redis::Error({Status::NotOK, s.Msg()});
     s = util::SockSend(fd, err_msg, ssl);
     if (!s.IsOK()) {
       LOG(WARNING) << "Failed to send error response to socket: " << s.Msg();
@@ -200,8 +200,7 @@ void Worker::newUnixSocketConnection(evconnlistener 
*listener, evutil_socket_t f
 
   auto s = AddConnection(conn);
   if (!s.IsOK()) {
-    std::string err_msg = redis::Error("ERR " + s.Msg());
-    s = util::SockSend(fd, err_msg);
+    s = util::SockSend(fd, redis::Error(s));
     if (!s.IsOK()) {
       LOG(WARNING) << "Failed to send error response to socket: " << s.Msg();
     }
diff --git a/src/storage/rdb.cc b/src/storage/rdb.cc
index f513caca..31c56f00 100644
--- a/src/storage/rdb.cc
+++ b/src/storage/rdb.cc
@@ -688,9 +688,9 @@ Status RDB::Dump(const std::string &key, const RedisType 
type) {
   /* Serialize the object in an RDB-like format. It consist of an object type
    * byte followed by the serialized object. This is understood by RESTORE. */
   auto s = SaveObjectType(type);
-  if (!s.IsOK()) return {Status::RedisExecErr, s.Msg()};
+  if (!s.IsOK()) return s;
   s = SaveObject(key, type);
-  if (!s.IsOK()) return {Status::RedisExecErr, s.Msg()};
+  if (!s.IsOK()) return s;
 
   /* Write the footer, this is how it looks like:
    * ----------------+---------------------+---------------+
@@ -705,21 +705,14 @@ Status RDB::Dump(const std::string &key, const RedisType 
type) {
   buf[0] = MinRDBVersion & 0xff;
   buf[1] = (MinRDBVersion >> 8) & 0xff;
   s = stream_->Write((const char *)buf, 2);
-  if (!s.IsOK()) {
-    return {Status::RedisExecErr, s.Msg()};
-  }
+  if (!s.IsOK()) return s;
 
   /* CRC64 */
   CHECK(dynamic_cast<RdbStringStream *>(stream_.get()) != nullptr);
   std::string &output = static_cast<RdbStringStream 
*>(stream_.get())->GetInput();
   uint64_t crc = crc64(0, (unsigned char *)(output.c_str()), output.length());
   memrev64ifbe(&crc);
-  s = stream_->Write((const char *)(&crc), 8);
-  if (!s.IsOK()) {
-    return {Status::RedisExecErr, s.Msg()};
-  }
-
-  return Status::OK();
+  return stream_->Write((const char *)(&crc), 8);
 }
 
 Status RDB::SaveObjectType(const RedisType type) {
@@ -797,48 +790,28 @@ Status RDB::RdbSaveLen(uint64_t len) {
   if (len < (1 << 6)) {
     /* Save a 6 bit len */
     buf[0] = (len & 0xFF) | (RDB6BitLen << 6);
-    auto status = stream_->Write((const char *)buf, 1);
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
-    return Status::OK();
+    return stream_->Write((const char *)buf, 1);
   } else if (len < (1 << 14)) {
     /* Save a 14 bit len */
     buf[0] = ((len >> 8) & 0xFF) | (RDB14BitLen << 6);
     buf[1] = len & 0xFF;
-    auto status = stream_->Write((const char *)buf, 2);
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
-    return Status::OK();
+    return stream_->Write((const char *)buf, 2);
   } else if (len <= UINT32_MAX) {
     /* Save a 32 bit len */
     buf[0] = RDB32BitLen;
     auto status = stream_->Write((const char *)buf, 1);
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    if (!status.IsOK()) return status;
 
     uint32_t len32 = htonl(len);
-    status = stream_->Write((const char *)(&len32), 4);
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
-    return Status::OK();
+    return stream_->Write((const char *)(&len32), 4);
   } else {
     /* Save a 64 bit len */
     buf[0] = RDB64BitLen;
     auto status = stream_->Write((const char *)buf, 1);
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    if (!status.IsOK()) return status;
 
     len = htonu64(len);
-    status = stream_->Write((const char *)(&len), 8);
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
-    return Status::OK();
+    return stream_->Write((const char *)(&len), 8);
   }
 }
 
@@ -857,11 +830,7 @@ Status RDB::SaveStringObject(const std::string &value) {
       // encode integer
       enclen = rdbEncodeInteger(integer_value, buf);
       if (enclen > 0) {
-        auto status = stream_->Write((const char *)buf, enclen);
-        if (!status.IsOK()) {
-          return {Status::RedisExecErr, status.Msg()};
-        }
-        return Status::OK();
+        return stream_->Write((const char *)buf, enclen);
       }
     }
   }
@@ -871,14 +840,9 @@ Status RDB::SaveStringObject(const std::string &value) {
 
   /* Store verbatim */
   auto status = RdbSaveLen(value.length());
-  if (!status.IsOK()) {
-    return {Status::RedisExecErr, status.Msg()};
-  }
+  if (!status.IsOK()) return status;
   if (value.length() > 0) {
-    status = stream_->Write(value.c_str(), value.length());
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    return stream_->Write(value.c_str(), value.length());
   }
   return Status::OK();
 }
@@ -886,15 +850,11 @@ Status RDB::SaveStringObject(const std::string &value) {
 Status RDB::SaveListObject(const std::vector<std::string> &elems) {
   if (elems.size() > 0) {
     auto status = RdbSaveLen(elems.size());
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    if (!status.IsOK()) return status;
 
     for (const auto &elem : elems) {
       auto status = rdbSaveZipListObject(elem);
-      if (!status.IsOK()) {
-        return {Status::RedisExecErr, status.Msg()};
-      }
+      if (!status.IsOK()) return status;
     }
   } else {
     LOG(WARNING) << "the size of elems is zero";
@@ -906,15 +866,11 @@ Status RDB::SaveListObject(const std::vector<std::string> 
&elems) {
 Status RDB::SaveSetObject(const std::vector<std::string> &members) {
   if (members.size() > 0) {
     auto status = RdbSaveLen(members.size());
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    if (!status.IsOK()) return status;
 
     for (const auto &elem : members) {
       status = SaveStringObject(elem);
-      if (!status.IsOK()) {
-        return {Status::RedisExecErr, status.Msg()};
-      }
+      if (!status.IsOK()) return status;
     }
   } else {
     LOG(WARNING) << "the size of elems is zero";
@@ -926,20 +882,14 @@ Status RDB::SaveSetObject(const std::vector<std::string> 
&members) {
 Status RDB::SaveZSetObject(const std::vector<MemberScore> &member_scores) {
   if (member_scores.size() > 0) {
     auto status = RdbSaveLen(member_scores.size());
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    if (!status.IsOK()) return status;
 
     for (const auto &elem : member_scores) {
       status = SaveStringObject(elem.member);
-      if (!status.IsOK()) {
-        return {Status::RedisExecErr, status.Msg()};
-      }
+      if (!status.IsOK()) return status;
 
       status = rdbSaveBinaryDoubleValue(elem.score);
-      if (!status.IsOK()) {
-        return {Status::RedisExecErr, status.Msg()};
-      }
+      if (!status.IsOK()) return status;
     }
   } else {
     LOG(WARNING) << "the size of member_scores is zero";
@@ -951,20 +901,14 @@ Status RDB::SaveZSetObject(const std::vector<MemberScore> 
&member_scores) {
 Status RDB::SaveHashObject(const std::vector<FieldValue> &field_values) {
   if (field_values.size() > 0) {
     auto status = RdbSaveLen(field_values.size());
-    if (!status.IsOK()) {
-      return {Status::RedisExecErr, status.Msg()};
-    }
+    if (!status.IsOK()) return status;
 
     for (const auto &p : field_values) {
       status = SaveStringObject(p.field);
-      if (!status.IsOK()) {
-        return {Status::RedisExecErr, status.Msg()};
-      }
+      if (!status.IsOK()) return status;
 
       status = SaveStringObject(p.value);
-      if (!status.IsOK()) {
-        return {Status::RedisExecErr, status.Msg()};
-      }
+      if (!status.IsOK()) return status;
     }
   } else {
     LOG(WARNING) << "the size of field_values is zero";
@@ -1024,10 +968,5 @@ Status RDB::rdbSaveZipListObject(const std::string &elem) 
{
   ZipList::SetZipListLength(zl_ptr, ziplist_size, 1);
   zl_ptr[ziplist_size - 1] = zlEnd;
 
-  auto status = SaveStringObject(zl_string);
-  if (!status.IsOK()) {
-    return {Status::RedisExecErr, status.Msg()};
-  }
-
-  return Status::OK();
+  return SaveStringObject(zl_string);
 }
diff --git a/src/storage/scripting.cc b/src/storage/scripting.cc
index 409d5e19..5197eb5c 100644
--- a/src/storage/scripting.cc
+++ b/src/storage/scripting.cc
@@ -611,8 +611,7 @@ Status EvalGenericCommand(redis::Connection *conn, const 
std::string &body_or_sh
       auto s = srv->ScriptGet(funcname + 2, &body);
       if (!s.IsOK()) {
         lua_pop(lua, 1); /* remove the error handler from the stack. */
-        *output = redis::Error(redis::errNoMatchingScript);
-        return Status::OK();
+        return {Status::RedisNoScript, redis::errNoMatchingScript};
       }
     } else {
       body = body_or_sha;
@@ -640,8 +639,8 @@ Status EvalGenericCommand(redis::Connection *conn, const 
std::string &body_or_sh
   SetGlobalArray(lua, "ARGV", argv);
 
   if (lua_pcall(lua, 0, 1, -2)) {
-    auto msg = fmt::format("ERR running script (call to {}): {}", funcname, 
lua_tostring(lua, -1));
-    *output = redis::Error(msg);
+    auto msg = fmt::format("running script (call to {}): {}", funcname, 
lua_tostring(lua, -1));
+    *output = redis::Error({Status::NotOK, msg});
     lua_pop(lua, 2);
   } else {
     *output = ReplyToRedisReply(conn, lua);
@@ -755,7 +754,7 @@ int RedisGenericCommand(lua_State *lua, int raise_error) {
   if (config->cluster_enabled) {
     auto s = srv->cluster->CanExecByMySelf(attributes, args, conn);
     if (!s.IsOK()) {
-      PushError(lua, s.Msg().c_str());
+      PushError(lua, redis::StatusToRedisErrorMsg(s).c_str());
       return raise_error ? RaiseError(lua) : 1;
     }
   }
@@ -1192,7 +1191,7 @@ std::string ReplyToRedisReply(redis::Connection *conn, 
lua_State *lua) {
       lua_rawget(lua, -2);
       t = lua_type(lua, -1);
       if (t == LUA_TSTRING) {
-        output = redis::Error(lua_tostring(lua, -1));
+        output = redis::Error({Status::RedisErrorNoPrefix, lua_tostring(lua, 
-1)});
         lua_pop(lua, 1);
         return output;
       }
diff --git a/src/types/json.h b/src/types/json.h
index 0ce88797..13abb5cc 100644
--- a/src/types/json.h
+++ b/src/types/json.h
@@ -238,7 +238,7 @@ struct JsonValue {
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-    if (!s) return {Status::NotOK, s.Msg()};
+    if (!s) return s;
 
     return results;
   }

Reply via email to