Repository: dragonflyoss/Dragonfly2
Pull request #1444 (2022-07-08 07:03:55)

feat: if peer back-to-source failed, return source metadata.
Signed-off-by: Gaius <gaius.qi@gmail.com>

<!--- Provide a general summary of your changes in the Title above -->

## Description
- If peer back-to-source failed, return source metadata.
<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.



Patch:
diff --git a/client/daemon/peer/peertask_conductor.go b/client/daemon/peer/peertask_conductor.go
index 4ce65848..c5dfbe8d 100644
--- a/client/daemon/peer/peertask_conductor.go
+++ b/client/daemon/peer/peertask_conductor.go
@@ -30,6 +30,7 @@ import (
 	"go.opentelemetry.io/otel/trace"
 	"go.uber.org/atomic"
 	"golang.org/x/time/rate"
+	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
 	"d7y.io/dragonfly/v2/client/config"
@@ -805,6 +806,16 @@ func (pt *peerTaskConductor) isExitPeerPacketCode(pp *scheduler.PeerPacket) bool
 		pt.failedCode = pp.Code
 		pt.failedReason = fmt.Sprintf("receive exit peer packet with code %d", pp.Code)
 		return true
+	case base.Code_BackToSourceAborted:
+		st := status.Newf(codes.Aborted, "response is not valid")
+		st, err := st.WithDetails(pp.GetSourceError())
+		if err != nil {
+			pt.Errorf("convert source error details error: %s", err.Error())
+			return false
+		}
+
+		pt.sourceErrorStatus = st
+		return true
 	}
 	return false
 }
diff --git a/pkg/rpc/base/base.pb.go b/pkg/rpc/base/base.pb.go
index 23900ffa..b8d5937a 100644
--- a/pkg/rpc/base/base.pb.go
+++ b/pkg/rpc/base/base.pb.go
@@ -46,11 +46,12 @@ const (
 	Code_ServerUnavailable Code = 500
 	// common response error 1000-1999
 	// client can be migrated to another scheduler/CDN
-	Code_ResourceLacked   Code = 1000
-	Code_BadRequest       Code = 1400
-	Code_PeerTaskNotFound Code = 1404
-	Code_UnknownError     Code = 1500
-	Code_RequestTimeOut   Code = 1504
+	Code_ResourceLacked      Code = 1000
+	Code_BackToSourceAborted Code = 1001
+	Code_BadRequest          Code = 1400
+	Code_PeerTaskNotFound    Code = 1404
+	Code_UnknownError        Code = 1500
+	Code_RequestTimeOut      Code = 1504
 	// client response error 4000-4999
 	Code_ClientError             Code = 4000
 	Code_ClientPieceRequestFail  Code = 4001 // get piece task from other peer error
@@ -83,6 +84,7 @@ var (
 		200:  "Success",
 		500:  "ServerUnavailable",
 		1000: "ResourceLacked",
+		1001: "BackToSourceAborted",
 		1400: "BadRequest",
 		1404: "PeerTaskNotFound",
 		1500: "UnknownError",
@@ -112,6 +114,7 @@ var (
 		"Success":                        200,
 		"ServerUnavailable":              500,
 		"ResourceLacked":                 1000,
+		"BackToSourceAborted":            1001,
 		"BadRequest":                     1400,
 		"PeerTaskNotFound":               1404,
 		"UnknownError":                   1500,
@@ -1036,61 +1039,63 @@ var file_pkg_rpc_base_base_proto_rawDesc = []byte{
 	0x75, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x61, 0x73, 0x65,
 	0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
 	0x52, 0x0f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
-	0x65, 0x2a, 0x94, 0x05, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x58, 0x5f,
+	0x65, 0x2a, 0xae, 0x05, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x58, 0x5f,
 	0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a,
 	0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0xc8, 0x01, 0x12, 0x16, 0x0a, 0x11, 0x53,
 	0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65,
 	0x10, 0xf4, 0x03, 0x12, 0x13, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c,
-	0x61, 0x63, 0x6b, 0x65, 0x64, 0x10, 0xe8, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x42, 0x61, 0x64, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xf8, 0x0a, 0x12, 0x15, 0x0a, 0x10, 0x50, 0x65, 0x65,
-	0x72, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0xfc, 0x0a,
-	0x12, 0x11, 0x0a, 0x0c, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72,
-	0x10, 0xdc, 0x0b, 0x12, 0x13, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69,
-	0x6d, 0x65, 0x4f, 0x75, 0x74, 0x10, 0xe0, 0x0b, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x6c, 0x69, 0x65,
-	0x6e, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0xa0, 0x1f, 0x12, 0x1b, 0x0a, 0x16, 0x43, 0x6c,
-	0x69, 0x65, 0x6e, 0x74, 0x50, 0x69, 0x65, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x46, 0x61, 0x69, 0x6c, 0x10, 0xa1, 0x1f, 0x12, 0x1a, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e,
-	0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
-	0x10, 0xa2, 0x1f, 0x12, 0x1a, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e,
-	0x74, 0x65, 0x78, 0x74, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x10, 0xa3, 0x1f, 0x12,
-	0x19, 0x0a, 0x14, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x57, 0x61, 0x69, 0x74, 0x50, 0x69, 0x65,
-	0x63, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x10, 0xa4, 0x1f, 0x12, 0x1c, 0x0a, 0x17, 0x43, 0x6c,
-	0x69, 0x65, 0x6e, 0x74, 0x50, 0x69, 0x65, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61,
-	0x64, 0x46, 0x61, 0x69, 0x6c, 0x10, 0xa5, 0x1f, 0x12, 0x1b, 0x0a, 0x16, 0x43, 0x6c, 0x69, 0x65,
-	0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61,
-	0x69, 0x6c, 0x10, 0xa6, 0x1f, 0x12, 0x1a, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43,
-	0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0xa7,
-	0x1f, 0x12, 0x1a, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x53,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0xa8, 0x1f, 0x12, 0x18, 0x0a,
-	0x13, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x69, 0x65, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x46,
-	0x6f, 0x75, 0x6e, 0x64, 0x10, 0xb4, 0x22, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x63, 0x68, 0x65, 0x64,
-	0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x88, 0x27, 0x12, 0x18, 0x0a, 0x13, 0x53, 0x63, 0x68, 0x65,
-	0x64, 0x4e, 0x65, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10,
-	0x89, 0x27, 0x12, 0x12, 0x0a, 0x0d, 0x53, 0x63, 0x68, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x47,
-	0x6f, 0x6e, 0x65, 0x10, 0x8a, 0x27, 0x12, 0x16, 0x0a, 0x11, 0x53, 0x63, 0x68, 0x65, 0x64, 0x50,
-	0x65, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x8c, 0x27, 0x12, 0x23,
-	0x0a, 0x1e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x50, 0x69, 0x65, 0x63, 0x65,
-	0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x61, 0x69, 0x6c,
-	0x10, 0x8d, 0x27, 0x12, 0x19, 0x0a, 0x14, 0x53, 0x63, 0x68, 0x65, 0x64, 0x54, 0x61, 0x73, 0x6b,
-	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x8e, 0x27, 0x12, 0x18,
-	0x0a, 0x13, 0x43, 0x44, 0x4e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
-	0x79, 0x46, 0x61, 0x69, 0x6c, 0x10, 0xf1, 0x2e, 0x12, 0x14, 0x0a, 0x0f, 0x43, 0x44, 0x4e, 0x54,
-	0x61, 0x73, 0x6b, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x84, 0x32, 0x12, 0x18,
-	0x0a, 0x13, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xd9, 0x36, 0x2a, 0x17, 0x0a, 0x0a, 0x50, 0x69, 0x65, 0x63,
-	0x65, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10,
-	0x00, 0x2a, 0x2c, 0x0a, 0x09, 0x53, 0x69, 0x7a, 0x65, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x0a,
-	0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4d,
-	0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x49, 0x4e, 0x59, 0x10, 0x02, 0x2a,
-	0x2d, 0x0a, 0x07, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x32,
-	0x50, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x45, 0x45, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52,
-	0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x02, 0x2a, 0x30,
-	0x0a, 0x08, 0x54, 0x61, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x6f,
-	0x72, 0x6d, 0x61, 0x6c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x66, 0x43, 0x61, 0x63, 0x68,
-	0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x10, 0x02,
-	0x42, 0x22, 0x5a, 0x20, 0x64, 0x37, 0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x64, 0x72, 0x61, 0x67, 0x6f,
-	0x6e, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x72, 0x70, 0x63, 0x2f,
-	0x62, 0x61, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x61, 0x63, 0x6b, 0x65, 0x64, 0x10, 0xe8, 0x07, 0x12, 0x18, 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b,
+	0x54, 0x6f, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x10,
+	0xe9, 0x07, 0x12, 0x0f, 0x0a, 0x0a, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x10, 0xf8, 0x0a, 0x12, 0x15, 0x0a, 0x10, 0x50, 0x65, 0x65, 0x72, 0x54, 0x61, 0x73, 0x6b, 0x4e,
+	0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0xfc, 0x0a, 0x12, 0x11, 0x0a, 0x0c, 0x55, 0x6e,
+	0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0xdc, 0x0b, 0x12, 0x13, 0x0a,
+	0x0e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x4f, 0x75, 0x74, 0x10,
+	0xe0, 0x0b, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x45, 0x72, 0x72, 0x6f,
+	0x72, 0x10, 0xa0, 0x1f, 0x12, 0x1b, 0x0a, 0x16, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x69,
+	0x65, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x10, 0xa1,
+	0x1f, 0x12, 0x1a, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64,
+	0x75, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x10, 0xa2, 0x1f, 0x12, 0x1a, 0x0a,
+	0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x43, 0x61,
+	0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x10, 0xa3, 0x1f, 0x12, 0x19, 0x0a, 0x14, 0x43, 0x6c, 0x69,
+	0x65, 0x6e, 0x74, 0x57, 0x61, 0x69, 0x74, 0x50, 0x69, 0x65, 0x63, 0x65, 0x52, 0x65, 0x61, 0x64,
+	0x79, 0x10, 0xa4, 0x1f, 0x12, 0x1c, 0x0a, 0x17, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x69,
+	0x65, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x10,
+	0xa5, 0x1f, 0x12, 0x1b, 0x0a, 0x16, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x10, 0xa6, 0x1f, 0x12,
+	0x1a, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0xa7, 0x1f, 0x12, 0x1a, 0x0a, 0x15, 0x43,
+	0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45,
+	0x72, 0x72, 0x6f, 0x72, 0x10, 0xa8, 0x1f, 0x12, 0x18, 0x0a, 0x13, 0x43, 0x6c, 0x69, 0x65, 0x6e,
+	0x74, 0x50, 0x69, 0x65, 0x63, 0x65, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0xb4,
+	0x22, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x63, 0x68, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10,
+	0x88, 0x27, 0x12, 0x18, 0x0a, 0x13, 0x53, 0x63, 0x68, 0x65, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x42,
+	0x61, 0x63, 0x6b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x10, 0x89, 0x27, 0x12, 0x12, 0x0a, 0x0d,
+	0x53, 0x63, 0x68, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x47, 0x6f, 0x6e, 0x65, 0x10, 0x8a, 0x27,
+	0x12, 0x16, 0x0a, 0x11, 0x53, 0x63, 0x68, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x6f, 0x74,
+	0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x8c, 0x27, 0x12, 0x23, 0x0a, 0x1e, 0x53, 0x63, 0x68, 0x65,
+	0x64, 0x50, 0x65, 0x65, 0x72, 0x50, 0x69, 0x65, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
+	0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x10, 0x8d, 0x27, 0x12, 0x19, 0x0a,
+	0x14, 0x53, 0x63, 0x68, 0x65, 0x64, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x8e, 0x27, 0x12, 0x18, 0x0a, 0x13, 0x43, 0x44, 0x4e, 0x54,
+	0x61, 0x73, 0x6b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x46, 0x61, 0x69, 0x6c, 0x10,
+	0xf1, 0x2e, 0x12, 0x14, 0x0a, 0x0f, 0x43, 0x44, 0x4e, 0x54, 0x61, 0x73, 0x6b, 0x4e, 0x6f, 0x74,
+	0x46, 0x6f, 0x75, 0x6e, 0x64, 0x10, 0x84, 0x32, 0x12, 0x18, 0x0a, 0x13, 0x49, 0x6e, 0x76, 0x61,
+	0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10,
+	0xd9, 0x36, 0x2a, 0x17, 0x0a, 0x0a, 0x50, 0x69, 0x65, 0x63, 0x65, 0x53, 0x74, 0x79, 0x6c, 0x65,
+	0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x2a, 0x2c, 0x0a, 0x09, 0x53,
+	0x69, 0x7a, 0x65, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d,
+	0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4d, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12,
+	0x08, 0x0a, 0x04, 0x54, 0x49, 0x4e, 0x59, 0x10, 0x02, 0x2a, 0x2d, 0x0a, 0x07, 0x50, 0x61, 0x74,
+	0x74, 0x65, 0x72, 0x6e, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x32, 0x50, 0x10, 0x00, 0x12, 0x0d, 0x0a,
+	0x09, 0x53, 0x45, 0x45, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06,
+	0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x02, 0x2a, 0x30, 0x0a, 0x08, 0x54, 0x61, 0x73, 0x6b,
+	0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x10, 0x00,
+	0x12, 0x0b, 0x0a, 0x07, 0x44, 0x66, 0x43, 0x61, 0x63, 0x68, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a,
+	0x07, 0x44, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x10, 0x02, 0x42, 0x22, 0x5a, 0x20, 0x64, 0x37,
+	0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x64, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x66, 0x6c, 0x79, 0x2f, 0x76,
+	0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x62, 0x06,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/pkg/rpc/base/base.proto b/pkg/rpc/base/base.proto
index b9a78bda..e0f39f8f 100644
--- a/pkg/rpc/base/base.proto
+++ b/pkg/rpc/base/base.proto
@@ -32,6 +32,7 @@ enum Code{
   // common response error 1000-1999
   // client can be migrated to another scheduler/CDN
   ResourceLacked = 1000;
+  BackToSourceAborted = 1001;
   BadRequest = 1400;
   PeerTaskNotFound = 1404;
   UnknownError = 1500;
diff --git a/pkg/rpc/scheduler/scheduler.pb.go b/pkg/rpc/scheduler/scheduler.pb.go
index 95e6efbb..1b8131ab 100644
--- a/pkg/rpc/scheduler/scheduler.pb.go
+++ b/pkg/rpc/scheduler/scheduler.pb.go
@@ -623,6 +623,11 @@ type PeerPacket struct {
 	StealPeers []*PeerPacket_DestPeer `protobuf:"bytes,6,rep,name=steal_peers,json=stealPeers,proto3" json:"steal_peers,omitempty"`
 	// Result code.
 	Code base.Code `protobuf:"varint,7,opt,name=code,proto3,enum=base.Code" json:"code,omitempty"`
+	// Error detail.
+	//
+	// Types that are assignable to ErrorDetail:
+	//	*PeerPacket_SourceError
+	ErrorDetail isPeerPacket_ErrorDetail `protobuf_oneof:"error_detail"`
 }
 
 func (x *PeerPacket) Reset() {
@@ -699,6 +704,31 @@ func (x *PeerPacket) GetCode() base.Code {
 	return base.Code(0)
 }
 
+func (m *PeerPacket) GetErrorDetail() isPeerPacket_ErrorDetail {
+	if m != nil {
+		return m.ErrorDetail
+	}
+	return nil
+}
+
+func (x *PeerPacket) GetSourceError() *errordetails.SourceError {
+	if x, ok := x.GetErrorDetail().(*PeerPacket_SourceError); ok {
+		return x.SourceError
+	}
+	return nil
+}
+
+type isPeerPacket_ErrorDetail interface {
+	isPeerPacket_ErrorDetail()
+}
+
+type PeerPacket_SourceError struct {
+	// Source error.
+	SourceError *errordetails.SourceError `protobuf:"bytes,8,opt,name=source_error,json=sourceError,proto3,oneof"`
+}
+
+func (*PeerPacket_SourceError) isPeerPacket_ErrorDetail() {}
+
 // PeerResult represents response of ReportPeerResult.
 type PeerResult struct {
 	state         protoimpl.MessageState
@@ -729,6 +759,8 @@ type PeerResult struct {
 	Code base.Code `protobuf:"varint,11,opt,name=code,proto3,enum=base.Code" json:"code,omitempty"`
 	// Task total piece count.
 	TotalPieceCount int32 `protobuf:"varint,12,opt,name=total_piece_count,json=totalPieceCount,proto3" json:"total_piece_count,omitempty"`
+	// Error detail.
+	//
 	// Types that are assignable to ErrorDetail:
 	//	*PeerResult_SourceError
 	ErrorDetail isPeerResult_ErrorDetail `protobuf_oneof:"error_detail"`
@@ -1354,7 +1386,7 @@ var file_pkg_rpc_scheduler_scheduler_proto_rawDesc = []byte{
 	0x74, 0x65, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x0b,
 	0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65,
 	0x6e, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x0f, 0x65, 0x78, 0x74,
-	0x65, 0x6e, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x8e, 0x03, 0x0a,
+	0x65, 0x6e, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0xde, 0x03, 0x0a,
 	0x0a, 0x50, 0x65, 0x65, 0x72, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74,
 	0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42,
 	0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x20, 0x0a,
@@ -1372,14 +1404,19 @@ var file_pkg_rpc_scheduler_scheduler_proto_rawDesc = []byte{
 	0x65, 0x72, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x50, 0x65, 0x65,
 	0x72, 0x52, 0x0a, 0x73, 0x74, 0x65, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a,
 	0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x62, 0x61,
-	0x73, 0x65, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x1a, 0x6e, 0x0a,
+	0x73, 0x65, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3e, 0x0a,
+	0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x08, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69,
+	0x6c, 0x73, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00,
+	0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x6e, 0x0a,
 	0x08, 0x44, 0x65, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70, 0x18,
 	0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x70, 0x01, 0x52, 0x02,
 	0x69, 0x70, 0x12, 0x27, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02,
 	0x20, 0x01, 0x28, 0x05, 0x42, 0x0c, 0xfa, 0x42, 0x09, 0x1a, 0x07, 0x10, 0xff, 0xff, 0x03, 0x28,
 	0x80, 0x08, 0x52, 0x07, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x70,
 	0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42,
-	0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x22, 0xf6, 0x03,
+	0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x42, 0x0e, 0x0a,
+	0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x22, 0xf6, 0x03,
 	0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x20, 0x0a, 0x07,
 	0x74, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa,
 	0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x20,
@@ -1543,30 +1580,31 @@ var file_pkg_rpc_scheduler_scheduler_proto_depIdxs = []int32{
 	11, // 13: scheduler.PeerPacket.main_peer:type_name -> scheduler.PeerPacket.DestPeer
 	11, // 14: scheduler.PeerPacket.steal_peers:type_name -> scheduler.PeerPacket.DestPeer
 	19, // 15: scheduler.PeerPacket.code:type_name -> base.Code
-	19, // 16: scheduler.PeerResult.code:type_name -> base.Code
-	20, // 17: scheduler.PeerResult.source_error:type_name -> errordetails.SourceError
-	15, // 18: scheduler.Task.type:type_name -> base.TaskType
-	12, // 19: scheduler.AnnounceTaskRequest.url_meta:type_name -> base.UrlMeta
-	3,  // 20: scheduler.AnnounceTaskRequest.peer_host:type_name -> scheduler.PeerHost
-	21, // 21: scheduler.AnnounceTaskRequest.piece_packet:type_name -> base.PiecePacket
-	15, // 22: scheduler.AnnounceTaskRequest.task_type:type_name -> base.TaskType
-	0,  // 23: scheduler.Scheduler.RegisterPeerTask:input_type -> scheduler.PeerTaskRequest
-	4,  // 24: scheduler.Scheduler.ReportPieceResult:input_type -> scheduler.PieceResult
-	6,  // 25: scheduler.Scheduler.ReportPeerResult:input_type -> scheduler.PeerResult
-	7,  // 26: scheduler.Scheduler.LeaveTask:input_type -> scheduler.PeerTarget
-	8,  // 27: scheduler.Scheduler.StatTask:input_type -> scheduler.StatTaskRequest
-	10, // 28: scheduler.Scheduler.AnnounceTask:input_type -> scheduler.AnnounceTaskRequest
-	1,  // 29: scheduler.Scheduler.RegisterPeerTask:output_type -> scheduler.RegisterResult
-	5,  // 30: scheduler.Scheduler.ReportPieceResult:output_type -> scheduler.PeerPacket
-	22, // 31: scheduler.Scheduler.ReportPeerResult:output_type -> google.protobuf.Empty
-	22, // 32: scheduler.Scheduler.LeaveTask:output_type -> google.protobuf.Empty
-	9,  // 33: scheduler.Scheduler.StatTask:output_type -> scheduler.Task
-	22, // 34: scheduler.Scheduler.AnnounceTask:output_type -> google.protobuf.Empty
-	29, // [29:35] is the sub-list for method output_type
-	23, // [23:29] is the sub-list for method input_type
-	23, // [23:23] is the sub-list for extension type_name
-	23, // [23:23] is the sub-list for extension extendee
-	0,  // [0:23] is the sub-list for field type_name
+	20, // 16: scheduler.PeerPacket.source_error:type_name -> errordetails.SourceError
+	19, // 17: scheduler.PeerResult.code:type_name -> base.Code
+	20, // 18: scheduler.PeerResult.source_error:type_name -> errordetails.SourceError
+	15, // 19: scheduler.Task.type:type_name -> base.TaskType
+	12, // 20: scheduler.AnnounceTaskRequest.url_meta:type_name -> base.UrlMeta
+	3,  // 21: scheduler.AnnounceTaskRequest.peer_host:type_name -> scheduler.PeerHost
+	21, // 22: scheduler.AnnounceTaskRequest.piece_packet:type_name -> base.PiecePacket
+	15, // 23: scheduler.AnnounceTaskRequest.task_type:type_name -> base.TaskType
+	0,  // 24: scheduler.Scheduler.RegisterPeerTask:input_type -> scheduler.PeerTaskRequest
+	4,  // 25: scheduler.Scheduler.ReportPieceResult:input_type -> scheduler.PieceResult
+	6,  // 26: scheduler.Scheduler.ReportPeerResult:input_type -> scheduler.PeerResult
+	7,  // 27: scheduler.Scheduler.LeaveTask:input_type -> scheduler.PeerTarget
+	8,  // 28: scheduler.Scheduler.StatTask:input_type -> scheduler.StatTaskRequest
+	10, // 29: scheduler.Scheduler.AnnounceTask:input_type -> scheduler.AnnounceTaskRequest
+	1,  // 30: scheduler.Scheduler.RegisterPeerTask:output_type -> scheduler.RegisterResult
+	5,  // 31: scheduler.Scheduler.ReportPieceResult:output_type -> scheduler.PeerPacket
+	22, // 32: scheduler.Scheduler.ReportPeerResult:output_type -> google.protobuf.Empty
+	22, // 33: scheduler.Scheduler.LeaveTask:output_type -> google.protobuf.Empty
+	9,  // 34: scheduler.Scheduler.StatTask:output_type -> scheduler.Task
+	22, // 35: scheduler.Scheduler.AnnounceTask:output_type -> google.protobuf.Empty
+	30, // [30:36] is the sub-list for method output_type
+	24, // [24:30] is the sub-list for method input_type
+	24, // [24:24] is the sub-list for extension type_name
+	24, // [24:24] is the sub-list for extension extendee
+	0,  // [0:24] is the sub-list for field type_name
 }
 
 func init() { file_pkg_rpc_scheduler_scheduler_proto_init() }
@@ -1724,6 +1762,9 @@ func file_pkg_rpc_scheduler_scheduler_proto_init() {
 		(*RegisterResult_SinglePiece)(nil),
 		(*RegisterResult_PieceContent)(nil),
 	}
+	file_pkg_rpc_scheduler_scheduler_proto_msgTypes[5].OneofWrappers = []interface{}{
+		(*PeerPacket_SourceError)(nil),
+	}
 	file_pkg_rpc_scheduler_scheduler_proto_msgTypes[6].OneofWrappers = []interface{}{
 		(*PeerResult_SourceError)(nil),
 	}
diff --git a/pkg/rpc/scheduler/scheduler.pb.validate.go b/pkg/rpc/scheduler/scheduler.pb.validate.go
index b13ffd38..8987d1bb 100644
--- a/pkg/rpc/scheduler/scheduler.pb.validate.go
+++ b/pkg/rpc/scheduler/scheduler.pb.validate.go
@@ -697,6 +697,22 @@ func (m *PeerPacket) Validate() error {
 
 	// no validation rules for Code
 
+	switch m.ErrorDetail.(type) {
+
+	case *PeerPacket_SourceError:
+
+		if v, ok := interface{}(m.GetSourceError()).(interface{ Validate() error }); ok {
+			if err := v.Validate(); err != nil {
+				return PeerPacketValidationError{
+					field:  "SourceError",
+					reason: "embedded message failed validation",
+					cause:  err,
+				}
+			}
+		}
+
+	}
+
 	return nil
 }
 
diff --git a/pkg/rpc/scheduler/scheduler.proto b/pkg/rpc/scheduler/scheduler.proto
index 47631172..e7d064bc 100644
--- a/pkg/rpc/scheduler/scheduler.proto
+++ b/pkg/rpc/scheduler/scheduler.proto
@@ -147,6 +147,11 @@ message PeerPacket{
   repeated DestPeer steal_peers = 6;
   // Result code.
   base.Code code = 7;
+  // Error detail.
+  oneof error_detail{
+    // Source error.
+    errordetails.SourceError source_error = 8;
+  }
 }
 
 // PeerResult represents response of ReportPeerResult.
@@ -175,6 +180,7 @@ message PeerResult{
   base.Code code = 11;
   // Task total piece count.
   int32 total_piece_count = 12 [(validate.rules).int32.gte = -1];
+  // Error detail.
   oneof error_detail{
     // Source error.
     errordetails.SourceError source_error = 13;
diff --git a/scheduler/resource/task.go b/scheduler/resource/task.go
index 0511dab9..81c50a0d 100644
--- a/scheduler/resource/task.go
+++ b/scheduler/resource/task.go
@@ -325,7 +325,7 @@ func (t *Task) CanBackToSource() bool {
 }
 
 // NotifyPeers notify all peers in the task with the state code.
-func (t *Task) NotifyPeers(code base.Code, event string) {
+func (t *Task) NotifyPeers(peerPacket *rpcscheduler.PeerPacket, event string) {
 	t.Peers.Range(func(_, value any) bool {
 		peer := value.(*Peer)
 		if peer.FSM.Is(PeerStateRunning) {
@@ -334,11 +334,11 @@ func (t *Task) NotifyPeers(code base.Code, event string) {
 				return true
 			}
 
-			if err := stream.Send(&rpcscheduler.PeerPacket{Code: code}); err != nil {
+			if err := stream.Send(peerPacket); err != nil {
 				t.Log.Errorf("send packet to peer %s failed: %s", peer.ID, err.Error())
 				return true
 			}
-			t.Log.Infof("task notify peer %s code %s", peer.ID, code)
+			t.Log.Infof("task notify peer %s code %s", peer.ID, peerPacket.Code)
 
 			if err := peer.FSM.Event(event); err != nil {
 				peer.Log.Errorf("peer fsm event failed: %s", err.Error())
diff --git a/scheduler/service/service.go b/scheduler/service/service.go
index 49c21333..6c039f5c 100644
--- a/scheduler/service/service.go
+++ b/scheduler/service/service.go
@@ -23,12 +23,14 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/trace"
+	"google.golang.org/grpc/status"
 
 	"d7y.io/dragonfly/v2/internal/dferrors"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/pkg/container/set"
 	"d7y.io/dragonfly/v2/pkg/rpc/base"
 	"d7y.io/dragonfly/v2/pkg/rpc/base/common"
+	"d7y.io/dragonfly/v2/pkg/rpc/errordetails"
 	rpcscheduler "d7y.io/dragonfly/v2/pkg/rpc/scheduler"
 	pkgtime "d7y.io/dragonfly/v2/pkg/time"
 	"d7y.io/dragonfly/v2/scheduler/config"
@@ -348,7 +350,7 @@ func (s *Service) ReportPeerResult(ctx context.Context, req *rpcscheduler.PeerRe
 			s.createRecord(peer, storage.PeerStateBackToSourceFailed, req)
 			metrics.DownloadFailureCount.WithLabelValues(peer.BizTag, metrics.DownloadFailureBackToSourceType).Inc()
 
-			s.handleTaskFail(ctx, peer.Task)
+			s.handleTaskFail(ctx, peer.Task, req.GetSourceError(), nil)
 			s.handlePeerFail(ctx, peer)
 			return nil
 		}
@@ -570,7 +572,7 @@ func (s *Service) triggerSeedPeerTask(ctx context.Context, task *resource.Task)
 		trace.ContextWithSpanContext(context.Background(), trace.SpanContextFromContext(ctx)), task)
 	if err != nil {
 		task.Log.Errorf("trigger seed peer download task failed: %s", err.Error())
-		s.handleTaskFail(ctx, task)
+		s.handleTaskFail(ctx, task, nil, err)
 		return
 	}
 
@@ -786,12 +788,47 @@ func (s *Service) handleTaskSuccess(ctx context.Context, task *resource.Task, re
 // Conditions for the task to switch to the TaskStateSucceeded are:
 // 1. Seed peer downloads the resource falied.
 // 2. Dfdaemon back-to-source to download failed.
-func (s *Service) handleTaskFail(ctx context.Context, task *resource.Task) {
-	// If the number of failed peers in the task is greater than FailedPeerCountLimit,
-	// then scheduler notifies running peers of failure.
-	if task.PeerFailedCount.Load() > resource.FailedPeerCountLimit {
+func (s *Service) handleTaskFail(ctx context.Context, task *resource.Task, backToSourceErr *errordetails.SourceError, seedPeerErr error) {
+	// If peer back-to-source fails due to an unrecoverable error,
+	// notify other peers of the failure,
+	// and return the source metadata to peer.
+	if backToSourceErr != nil {
+		if !backToSourceErr.Temporary {
+			task.NotifyPeers(&rpcscheduler.PeerPacket{
+				Code: base.Code_BackToSourceAborted,
+				ErrorDetail: &rpcscheduler.PeerPacket_SourceError{
+					SourceError: backToSourceErr,
+				},
+			}, resource.PeerEventDownloadFailed)
+			task.PeerFailedCount.Store(0)
+		}
+	} else if seedPeerErr != nil {
+		// If seed peer back-to-source fails due to an unrecoverable error,
+		// notify other peers of the failure,
+		// and return the source metadata to peer.
+		if st, ok := status.FromError(seedPeerErr); ok {
+			for _, detail := range st.Details() {
+				switch d := detail.(type) {
+				case *errordetails.SourceError:
+					if !d.Temporary {
+						task.NotifyPeers(&rpcscheduler.PeerPacket{
+							Code: base.Code_BackToSourceAborted,
+							ErrorDetail: &rpcscheduler.PeerPacket_SourceError{
+								SourceError: d,
+							},
+						}, resource.PeerEventDownloadFailed)
+						task.PeerFailedCount.Store(0)
+					}
+				}
+			}
+		}
+	} else if task.PeerFailedCount.Load() > resource.FailedPeerCountLimit {
+		// If the number of failed peers in the task is greater than FailedPeerCountLimit,
+		// then scheduler notifies running peers of failure.
+		task.NotifyPeers(&rpcscheduler.PeerPacket{
+			Code: base.Code_SchedTaskStatusError,
+		}, resource.PeerEventDownloadFailed)
 		task.PeerFailedCount.Store(0)
-		task.NotifyPeers(base.Code_SchedTaskStatusError, resource.PeerEventDownloadFailed)
 	}
 
 	if task.FSM.Is(resource.TaskStateFailed) {


Test patch:
diff --git a/scheduler/resource/task_test.go b/scheduler/resource/task_test.go
index 02a5a214..c7fa5f61 100644
--- a/scheduler/resource/task_test.go
+++ b/scheduler/resource/task_test.go
@@ -885,7 +885,7 @@ func TestTask_NotifyPeers(t *testing.T) {
 			name: "peer state is PeerStatePending",
 			run: func(t *testing.T, task *Task, mockPeer *Peer, stream rpcscheduler.Scheduler_ReportPieceResultServer, ms *rpcschedulermocks.MockScheduler_ReportPieceResultServerMockRecorder) {
 				mockPeer.FSM.SetState(PeerStatePending)
-				task.NotifyPeers(base.Code_SchedTaskStatusError, PeerEventDownloadFailed)
+				task.NotifyPeers(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError}, PeerEventDownloadFailed)
 
 				assert := assert.New(t)
 				assert.True(mockPeer.FSM.Is(PeerStatePending))
@@ -895,7 +895,7 @@ func TestTask_NotifyPeers(t *testing.T) {
 			name: "peer state is PeerStateRunning and stream is empty",
 			run: func(t *testing.T, task *Task, mockPeer *Peer, stream rpcscheduler.Scheduler_ReportPieceResultServer, ms *rpcschedulermocks.MockScheduler_ReportPieceResultServerMockRecorder) {
 				mockPeer.FSM.SetState(PeerStateRunning)
-				task.NotifyPeers(base.Code_SchedTaskStatusError, PeerEventDownloadFailed)
+				task.NotifyPeers(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError}, PeerEventDownloadFailed)
 
 				assert := assert.New(t)
 				assert.True(mockPeer.FSM.Is(PeerStateRunning))
@@ -908,7 +908,7 @@ func TestTask_NotifyPeers(t *testing.T) {
 				mockPeer.StoreStream(stream)
 				ms.Send(gomock.Eq(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError})).Return(errors.New("foo")).Times(1)
 
-				task.NotifyPeers(base.Code_SchedTaskStatusError, PeerEventDownloadFailed)
+				task.NotifyPeers(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError}, PeerEventDownloadFailed)
 
 				assert := assert.New(t)
 				assert.True(mockPeer.FSM.Is(PeerStateRunning))
@@ -921,7 +921,7 @@ func TestTask_NotifyPeers(t *testing.T) {
 				mockPeer.StoreStream(stream)
 				ms.Send(gomock.Eq(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError})).Return(errors.New("foo")).Times(1)
 
-				task.NotifyPeers(base.Code_SchedTaskStatusError, PeerEventRegisterNormal)
+				task.NotifyPeers(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError}, PeerEventDownloadFailed)
 
 				assert := assert.New(t)
 				assert.True(mockPeer.FSM.Is(PeerStateRunning))
@@ -934,7 +934,7 @@ func TestTask_NotifyPeers(t *testing.T) {
 				mockPeer.StoreStream(stream)
 				ms.Send(gomock.Eq(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError})).Return(nil).Times(1)
 
-				task.NotifyPeers(base.Code_SchedTaskStatusError, PeerEventDownloadFailed)
+				task.NotifyPeers(&rpcscheduler.PeerPacket{Code: base.Code_SchedTaskStatusError}, PeerEventDownloadFailed)
 
 				assert := assert.New(t)
 				assert.True(mockPeer.FSM.Is(PeerStateFailed))
diff --git a/scheduler/service/service_test.go b/scheduler/service/service_test.go
index a798ed0e..425bf736 100644
--- a/scheduler/service/service_test.go
+++ b/scheduler/service/service_test.go
@@ -32,6 +32,8 @@ import (
 
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 
 	"d7y.io/dragonfly/v2/internal/dferrors"
 	"d7y.io/dragonfly/v2/manager/types"
@@ -39,6 +41,7 @@ import (
 	"d7y.io/dragonfly/v2/pkg/idgen"
 	"d7y.io/dragonfly/v2/pkg/rpc/base"
 	"d7y.io/dragonfly/v2/pkg/rpc/base/common"
+	"d7y.io/dragonfly/v2/pkg/rpc/errordetails"
 	rpcscheduler "d7y.io/dragonfly/v2/pkg/rpc/scheduler"
 	rpcschedulermocks "d7y.io/dragonfly/v2/pkg/rpc/scheduler/mocks"
 	"d7y.io/dragonfly/v2/scheduler/config"
@@ -2893,10 +2896,24 @@ func TestService_handleTaskSuccess(t *testing.T) {
 }
 
 func TestService_handleTaskFail(t *testing.T) {
+	rst := status.Newf(codes.Aborted, "response is not valid")
+	st, err := rst.WithDetails(&errordetails.SourceError{Temporary: false})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rtst := status.Newf(codes.Aborted, "response is not valid")
+	tst, err := rtst.WithDetails(&errordetails.SourceError{Temporary: true})
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	tests := []struct {
-		name   string
-		mock   func(task *resource.Task)
-		expect func(t *testing.T, task *resource.Task)
+		name            string
+		backToSourceErr *errordetails.SourceError
+		seedPeerErr     error
+		mock            func(task *resource.Task)
+		expect          func(t *testing.T, task *resource.Task)
 	}{
 		{
 			name: "task state is TaskStatePending",
@@ -2938,10 +2955,57 @@ func TestService_handleTaskFail(t *testing.T) {
 				assert.True(task.FSM.Is(resource.TaskStateFailed))
 			},
 		},
+		{
+			name:            "peer back-to-source fails due to an unrecoverable error",
+			backToSourceErr: &errordetails.SourceError{Temporary: false},
+			mock: func(task *resource.Task) {
+				task.FSM.SetState(resource.TaskStateRunning)
+			},
+			expect: func(t *testing.T, task *resource.Task) {
+				assert := assert.New(t)
+				assert.True(task.FSM.Is(resource.TaskStateFailed))
+				assert.Equal(task.PeerFailedCount.Load(), int32(0))
+			},
+		},
+		{
+			name:            "peer back-to-source fails due to an temporary error",
+			backToSourceErr: &errordetails.SourceError{Temporary: true},
+			mock: func(task *resource.Task) {
+				task.FSM.SetState(resource.TaskStateRunning)
+			},
+			expect: func(t *testing.T, task *resource.Task) {
+				assert := assert.New(t)
+				assert.True(task.FSM.Is(resource.TaskStateFailed))
+			},
+		},
+		{
+			name:        "seed peer back-to-source fails due to an unrecoverable error",
+			seedPeerErr: st.Err(),
+			mock: func(task *resource.Task) {
+				task.FSM.SetState(resource.TaskStateRunning)
+			},
+			expect: func(t *testing.T, task *resource.Task) {
+				assert := assert.New(t)
+				assert.True(task.FSM.Is(resource.TaskStateFailed))
+				assert.Equal(task.PeerFailedCount.Load(), int32(0))
+			},
+		},
+		{
+			name:        "seed peer back-to-source fails due to an temporary error",
+			seedPeerErr: tst.Err(),
+			mock: func(task *resource.Task) {
+				task.FSM.SetState(resource.TaskStateRunning)
+			},
+			expect: func(t *testing.T, task *resource.Task) {
+				assert := assert.New(t)
+				assert.True(task.FSM.Is(resource.TaskStateFailed))
+				assert.Equal(task.PeerFailedCount.Load(), int32(0))
+			},
+		},
 		{
 			name: "number of failed peers in the task is greater than FailedPeerCountLimit",
 			mock: func(task *resource.Task) {
-				task.FSM.SetState(resource.TaskStateFailed)
+				task.FSM.SetState(resource.TaskStateRunning)
 				task.PeerFailedCount.Store(201)
 			},
 			expect: func(t *testing.T, task *resource.Task) {
@@ -2964,7 +3028,7 @@ func TestService_handleTaskFail(t *testing.T) {
 			task := resource.NewTask(mockTaskID, mockTaskURL, base.TaskType_Normal, mockTaskURLMeta, resource.WithBackToSourceLimit(mockTaskBackToSourceLimit))
 
 			tc.mock(task)
-			svc.handleTaskFail(context.Background(), task)
+			svc.handleTaskFail(context.Background(), task, tc.backToSourceErr, tc.seedPeerErr)
 			tc.expect(t, task)
 		})
 	}