Skip to content

Commit c8706d0

Browse files
authored
Merge pull request #1143 from doitian/fix-979-cch-order-permanent-errors
fix: enhance permanent error detection in CCH outgoing payments
2 parents 04813f6 + d108174 commit c8706d0

File tree

1 file changed

+37
-5
lines changed

1 file changed

+37
-5
lines changed

crates/fiber-lib/src/cch/actions/send_outgoing_payment.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,17 @@ impl ActionExecutor for SendFiberOutgoingPaymentExecutor {
9999

100100
impl SendFiberOutgoingPaymentExecutor {
101101
fn is_permanent_error(err: &str) -> bool {
102-
// TODO: replace string matching with structured error codes from NetworkActor.
103-
err.contains("InvalidParameter")
102+
// InvalidParameter errors are permanent validation errors
103+
if err.contains("InvalidParameter") {
104+
return true;
105+
}
106+
107+
// Additional permanent errors that won't be fixed by retrying
108+
let err_lower = err.to_lowercase();
109+
err_lower.contains("invalid payment request")
110+
|| err_lower.contains("invoice expired")
111+
|| err_lower.contains("payment hash mismatch")
112+
|| err_lower.contains("no path found")
104113
}
105114
}
106115

@@ -143,14 +152,18 @@ impl ActionExecutor for SendLightningOutgoingPaymentExecutor {
143152
Some(Err(err)) => {
144153
let failure_reason =
145154
format!("SendLightningOutgoingPaymentExecutor failure: {:?}", err);
146-
if Self::is_permanent_error(err) {
155+
if Self::is_permanent_error(&err) {
147156
CchTrackingEvent::PaymentChanged {
148157
payment_hash: self.payment_hash,
149158
payment_preimage: None,
150159
status: PaymentStatus::Failed,
151160
failure_reason: Some(failure_reason),
152161
}
153162
} else {
163+
tracing::warn!(
164+
"SendLightningOutgoingPaymentExecutor transient error, will retry. code: {}, error: {}",
165+
err.code(), err.message()
166+
);
154167
return Err(anyhow!(failure_reason));
155168
}
156169
}
@@ -167,8 +180,27 @@ impl ActionExecutor for SendLightningOutgoingPaymentExecutor {
167180
}
168181

169182
impl SendLightningOutgoingPaymentExecutor {
170-
fn is_permanent_error(status: tonic::Status) -> bool {
171-
matches!(status.code(), tonic::Code::InvalidArgument)
183+
fn is_permanent_error(status: &tonic::Status) -> bool {
184+
// Check for explicit invalid argument errors
185+
if matches!(status.code(), tonic::Code::InvalidArgument) {
186+
return true;
187+
}
188+
189+
// LND often returns Unknown status for validation errors that are permanent.
190+
// Check the error message to identify these cases.
191+
if matches!(status.code(), tonic::Code::Unknown) {
192+
let msg = status.message().to_lowercase();
193+
// These are validation/policy errors that won't be fixed by retrying
194+
return msg.contains("self-payments not allowed")
195+
|| msg.contains("invoice is already paid")
196+
|| msg.contains("invoice expired")
197+
|| msg.contains("incorrect payment amount")
198+
|| msg.contains("payment hash mismatch")
199+
|| msg.contains("no route")
200+
|| msg.contains("unable to find a path to destination");
201+
}
202+
203+
false
172204
}
173205
}
174206

0 commit comments

Comments
 (0)