Log pending transaction finalization errors
What changed, and why it matters
This commit only adds more detailed logging when a pending multi-signature Bitcoin transaction fails to finalize. It does not change how transactions are validated, authorized, or executed. There is no security vulnerability being fixed here; it is purely an observability improvement for operators.
No security action required. Treat as routine logging improvement; review in normal release testing.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change extends existing warning/debug log messages in PendingTransactionService to include formatted PSBT finalization error details. A new private helper FormatFinalizationErrors sanitizes and truncates error messages before logging. No logic around signature thresholds, authorization, transaction broadcasting, or state transitions is modified.
Changed components
BTCPayServer/HostedServices/PendingTransactionService.csInspect captured patch +29 / −4
diff --git a/BTCPayServer/HostedServices/PendingTransactionService.cs b/BTCPayServer/HostedServices/PendingTransactionService.cs
index cb508b8..f00ba15 100644
--- a/BTCPayServer/HostedServices/PendingTransactionService.cs
+++ b/BTCPayServer/HostedServices/PendingTransactionService.cs
@@ -233,25 +233,30 @@ public class PendingTransactionService(
var failedInputIndexes = finalizationErrors is null or { Count: 0 }
? "unknown"
: string.Join(",", finalizationErrors.Select(error => error.InputIndex).Distinct());
+ var finalizationErrorDetails = FormatFinalizationErrors(finalizationErrors);
if ((blob.SignaturesCollected ?? 0) >= (blob.SignaturesNeeded ?? int.MaxValue))
{
logger.LogWarning(
"Finalization attempt failed for pending transaction {PendingTransactionId} despite signature progress " +
- "{SignaturesCollected}/{SignaturesNeeded}. Failed input indexes: {FailedInputIndexes}",
+ "{SignaturesCollected}/{SignaturesNeeded}. Failed input indexes: {FailedInputIndexes}. " +
+ "Errors: {FinalizationErrors}",
pendingTransaction.Id,
blob.SignaturesCollected,
blob.SignaturesNeeded,
- failedInputIndexes);
+ failedInputIndexes,
+ finalizationErrorDetails);
}
else
{
logger.LogDebug(
"Finalization attempt failed for pending transaction {PendingTransactionId}. Signature progress: " +
- "{SignaturesCollected}/{SignaturesNeeded}; failed input indexes: {FailedInputIndexes}",
+ "{SignaturesCollected}/{SignaturesNeeded}; failed input indexes: {FailedInputIndexes}. " +
+ "Errors: {FinalizationErrors}",
pendingTransaction.Id,
blob.SignaturesCollected,
blob.SignaturesNeeded,
- failedInputIndexes);
+ failedInputIndexes,
+ finalizationErrorDetails);
}
}
pendingTransaction.SetBlob(blob);
@@ -446,6 +451,26 @@ public class PendingTransactionService(
blob.SignaturesCollected = progress.SignaturesCollected;
}
+ private static string FormatFinalizationErrors(IList<PSBTError>? errors)
+ {
+ if (errors is null or { Count: 0 })
+ return "unknown";
+
+ const int maxErrors = 20;
+ const int maxMessageLength = 256;
+ var details = errors.Take(maxErrors).Select(error =>
+ {
+ var message = error.Message.Replace('\r', ' ').Replace('\n', ' ');
+ if (message.Length > maxMessageLength)
+ message = $"{message[..maxMessageLength]}…";
+ return $"input {error.InputIndex}: {message}";
+ });
+ var result = string.Join(" | ", details);
+ return errors.Count > maxErrors
+ ? $"{result} | {errors.Count - maxErrors} more error(s)"
+ : result;
+ }
+
private sealed record PendingTransactionSignatureProgress(
int SignaturesNeeded,
int SignaturesTotal,
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.