From d10d3893e3514fc9f3d55119cf7058e22d4059ba Mon Sep 17 00:00:00 2001 From: minibits-cash Date: Fri, 25 Sep 2026 10:18:38 +0200 Subject: [PATCH] Fix setting transaction ERROR or EXPIRED statuses --- src/services/wallet/sendTask.ts | 2 ++ src/services/wallet/transferTask.ts | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/services/wallet/sendTask.ts b/src/services/wallet/sendTask.ts index cd3dfa69..d05d2f53 100644 --- a/src/services/wallet/sendTask.ts +++ b/src/services/wallet/sendTask.ts @@ -88,6 +88,8 @@ export const sendTask = async function ( } as TransactionTaskResult } catch (e: any) { // Mark the tx ERROR if one was created during prepare. + // prepare() errors thrown after the draft exists carry its transactionId. + transactionIdForRecovery ??= e?.params?.transactionId if (transactionIdForRecovery) { const tx = transactionsStore.findById(transactionIdForRecovery) if (tx) { diff --git a/src/services/wallet/transferTask.ts b/src/services/wallet/transferTask.ts index e948304e..6bbd2042 100644 --- a/src/services/wallet/transferTask.ts +++ b/src/services/wallet/transferTask.ts @@ -100,6 +100,9 @@ export const transferTask = async function ( nwcEvent, } as TransactionTaskResult } catch (e: any) { + // prepare() may throw after creating the draft (e.g. expired invoice); its + // errors carry the transactionId so the draft still gets stamped ERROR/EXPIRED. + transactionIdForRecovery ??= e?.params?.transactionId let txAfterError = transactionIdForRecovery ? transactionsStore.findById(transactionIdForRecovery) : undefined @@ -111,15 +114,17 @@ export const transferTask = async function ( txAfterError.status !== TransactionStatus.RECOVERED && txAfterError.status !== TransactionStatus.ERROR ) { + // Only prepare's expiry check puts `expiry` in the error params. + const status = e?.params?.expiry ? TransactionStatus.EXPIRED : TransactionStatus.ERROR let transactionData: TransactionData[] = [] try { transactionData = JSON.parse(txAfterError.data) } catch {} transactionData.push({ - status: TransactionStatus.ERROR, + status, error: WalletUtils.formatError(e), createdAt: new Date(), }) txAfterError.update({ - status: TransactionStatus.ERROR, + status, data: JSON.stringify(transactionData), }) } @@ -216,6 +221,9 @@ export const transferOnchainTask = async function ( nwcEvent, } as TransactionTaskResult } catch (e: any) { + // prepare() may throw after creating the draft (e.g. expired invoice); its + // errors carry the transactionId so the draft still gets stamped ERROR/EXPIRED. + transactionIdForRecovery ??= e?.params?.transactionId const txAfterError = transactionIdForRecovery ? transactionsStore.findById(transactionIdForRecovery) : undefined @@ -228,15 +236,17 @@ export const transferOnchainTask = async function ( txAfterError.status !== TransactionStatus.RECOVERED && txAfterError.status !== TransactionStatus.ERROR ) { + // Only prepare's expiry check puts `expiry` in the error params. + const status = e?.params?.expiry ? TransactionStatus.EXPIRED : TransactionStatus.ERROR let transactionData: TransactionData[] = [] try { transactionData = JSON.parse(txAfterError.data) } catch {} transactionData.push({ - status: TransactionStatus.ERROR, + status, error: WalletUtils.formatError(e), createdAt: new Date(), }) txAfterError.update({ - status: TransactionStatus.ERROR, + status, data: JSON.stringify(transactionData), }) }