distinguish biz error from rpc wire error
diff --git a/cluster/cluster/failover/cluster_invoker.go b/cluster/cluster/failover/cluster_invoker.go
index e13dcf5..d70a399 100644
--- a/cluster/cluster/failover/cluster_invoker.go
+++ b/cluster/cluster/failover/cluster_invoker.go
@@ -19,6 +19,7 @@
 
 import (
 	"context"
+	"dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
 	"fmt"
 )
 
@@ -83,7 +84,7 @@
 		invoked = append(invoked, ivk)
 		// DO INVOKE
 		result = ivk.Invoke(ctx, invocation)
-		if result.Error() != nil {
+		if result.Error() != nil && !isBizError(result.Error()) {
 			providers = append(providers, ivk.GetURL().Key())
 			continue
 		}
@@ -100,13 +101,16 @@
 		}
 	}
 
-	return &protocol.RPCResult{
-		Err: perrors.Wrap(result.Error(), fmt.Sprintf("Failed to invoke the method %v in the service %v. "+
-			"Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. "+
-			"Last error is %+v.", methodName, invokerSvc, retries, providers, len(providers), len(invokers),
-			invokerUrl, ip, constant.Version, result.Error().Error()),
-		),
-	}
+	logger.Errorf(fmt.Sprintf("Failed to invoke the method %v in the service %v. "+
+		"Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. "+
+		"Last error is %+v.", methodName, invokerSvc, retries, providers, len(providers), len(invokers),
+		invokerUrl, ip, constant.Version, result.Error().Error()))
+
+	return result
+}
+
+func isBizError(err error) bool {
+	return triple_protocol.IsWireError(err) && triple_protocol.CodeOf(err) == triple_protocol.CodeBizError
 }
 
 func getRetries(invokers []protocol.Invoker, methodName string) int {
diff --git a/protocol/result.go b/protocol/result.go
index 904bce3..d86cac2 100644
--- a/protocol/result.go
+++ b/protocol/result.go
@@ -53,9 +53,10 @@
 
 // RPCResult is default RPC result.
 type RPCResult struct {
-	Attrs map[string]interface{}
-	Err   error
-	Rest  interface{}
+	Attrs  map[string]interface{}
+	BizErr error
+	Err    error
+	Rest   interface{}
 }
 
 // SetError sets error.
@@ -68,6 +69,16 @@
 	return r.Err
 }
 
+// SetBizError sets error.
+func (r *RPCResult) SetBizError(err error) {
+	r.BizErr = err
+}
+
+// BizError gets error. Replaced with error code in triple protocol since 3.2.0
+func (r *RPCResult) BizError() error {
+	return r.BizErr
+}
+
 // SetResult sets invoker result.
 func (r *RPCResult) SetResult(rest interface{}) {
 	r.Rest = rest
diff --git a/protocol/triple/triple_protocol/code.go b/protocol/triple/triple_protocol/code.go
index c66074a..60bc67f 100644
--- a/protocol/triple/triple_protocol/code.go
+++ b/protocol/triple/triple_protocol/code.go
@@ -104,6 +104,8 @@
 	// authentication credentials for the operation.
 	CodeUnauthenticated Code = 16
 
+	CodeBizError Code = 17
+
 	minCode = CodeCanceled
 	maxCode = CodeUnauthenticated
 )
diff --git a/server/server.go b/server/server.go
index 1931d6f..5d9281e 100644
--- a/server/server.go
+++ b/server/server.go
@@ -20,6 +20,7 @@
 
 import (
 	"context"
+	"dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
 	"fmt"
 	"sort"
 	"sync"
@@ -106,7 +107,9 @@
 	if method, ok := ii.methodMap[name]; ok {
 		res, err := method.MethodFunc(ctx, args, ii.svc)
 		result.SetResult(res)
-		result.SetError(err)
+		if err != nil {
+			result.SetError(triple_protocol.NewError(triple_protocol.CodeBizError, err))
+		}
 		return result
 	}
 	result.SetError(fmt.Errorf("no match method for %s", name))