View Issue Details

IDProjectCategoryView StatusLast Update
0012074Talerwallet (WebExtension)public2026-11-11 01:33
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Summary0012074: WebExtension: Deposit / Pay request with too little money says only "The wallet could not prepare …" -- the insufficient-balance wording exists but is used only for merchant payments (patch attached)
DescriptionWhen a deposit or the payment of a peer request (taler://pay-pull) is refused because the wallet does not have enough money, the web wallet shows only "The wallet could not prepare this deposit." / "The wallet could not prepare this peer payment." The reason (WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE / WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE) appears only inside the collapsed "Technical details". wallet-core attaches insufficientBalanceDetails to these errors, and the web UI already turns exactly that structure into translated sentences -- but only for merchant payments (7050).

Screenshots (demo backend, wallet holding KUDOS:20):
- before-web-1-deposit.png, before-web-2-deposit-details.png: deposit of KUDOS:50 -> "The wallet could not prepare this deposit."
- before-web-3-paypull.png: invoice of KUDOS:50 -> "The wallet could not prepare this peer payment." + "Withdraw funds"
- before-web-4-deposit-iban.png: deposit to an IBAN, which no exchange of this wallet serves -> the same generic sentence
- before-android-1-deposit.png (reference): Android shows "Available for transfer: ク23.00" and "Insufficient balance" under the amount
- before-android-2-paypull.png / -3-paypull-behind.png, before-ios-2-paypull.png: for the invoice Android says "Insufficient balance" (behind an error sheet with the code), iOS shows "Internal core error" (0011934); the iOS deposit screen (DepositAmountView.swift:100) says "You don't have enough <currency>."

Code (taler-typescript-core origin/master bf2e571a2, packages/wallet-webui/src/):
- ui/error.ts:22-79: errorMessage() maps WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE to insufficientBalanceMessage(); 7026 / 7027 / 7028 fall to the fallback
- ui/error.ts:81-101: errorGuidance() likewise only for 7050
- routes/App.tsx:1362-1380, 1416-1436 (deposit review/confirm), 2257-2271 (pay-pull prepare), 2388-2394 (pay-pull confirm) pass these errors through walletErrorHint()
wallet-core: deposits.ts:2775-2781 / 2886-2892 (7026 with insufficientBalanceDetails), pay-peer-pull-debit.ts:2087-2092 / 1797-1802 (7028).

Patch (attached, web-insufficient-balance-wording.patch, ui/error.ts + test/error.test.ts):
- 7026, 7027 and 7028 are handled like 7050: the existing insufficientBalanceMessage() and the existing "Withdraw funds or choose another payment option, then try again." guidance.
- A deposit refused with causeHint merchant-deposit-insufficient (wallet-core's verdict when no exchange can wire to that kind of account, e.g. an IBAN on an x-taler-bank exchange) would otherwise read "The available funds cannot be deposited by this merchant." plus withdraw advice; it gets "The exchanges of this wallet cannot deposit to this type of bank account." and no guidance. This is the only new string.
- Two unit tests for the mapping.

Verification: Chrome extension build; after-web-1-deposit.png and after-web-3-paypull.png: "The wallet has KUDOS:20 available, which is not enough for KUDOS:50." + guidance; after-web-4-deposit-iban.png: "The exchanges of this wallet cannot deposit to this type of bank account." test/error.test.ts 3/3, error-presentation 4/4, i18n-catalog tests pass (compiled with --noEmitOnError false, since master has 29 pre-existing type errors in other test files; none in the touched files).

Assisted by LLM
Steps To Reproduce1. Withdraw KUDOS:20 from https://bank.demo.taler.net/.
2. Deposit KUDOS:50 to an x-taler-bank account (e.g. open payto://x-taler-bank/bank.demo.taler.net/<account>?receiver-name=X) -> "Review deposit".
3. Let another wallet create an invoice for KUDOS:50 (taler://pay-pull/...) and open it.
4. Both show only "The wallet could not prepare …".
TagsNo tags attached.
Attached Files
before-web-1-deposit.png (122,019 bytes)   
before-web-1-deposit.png (122,019 bytes)   
before-web-2-deposit-details.png (173,754 bytes)   
before-web-2-deposit-details.png (173,754 bytes)   
before-web-3-paypull.png (59,614 bytes)   
before-web-3-paypull.png (59,614 bytes)   
before-web-4-deposit-iban.png (107,518 bytes)   
before-web-4-deposit-iban.png (107,518 bytes)   
before-android-1-deposit.png (113,309 bytes)   
before-android-1-deposit.png (113,309 bytes)   
before-android-2-paypull.png (94,374 bytes)   
before-android-2-paypull.png (94,374 bytes)   
before-ios-2-paypull.png (126,116 bytes)   
before-ios-2-paypull.png (126,116 bytes)   
after-web-1-deposit.png (142,266 bytes)   
after-web-1-deposit.png (142,266 bytes)   
after-web-3-paypull.png (78,776 bytes)   
after-web-3-paypull.png (78,776 bytes)   
after-web-4-deposit-iban.png (113,490 bytes)   
after-web-4-deposit-iban.png (113,490 bytes)   
web-insufficient-balance-wording.patch (6,149 bytes)   
diff --git a/packages/wallet-webui/src/ui/error.ts b/packages/wallet-webui/src/ui/error.ts
index 1450c2531..023c27ac5 100644
--- a/packages/wallet-webui/src/ui/error.ts
+++ b/packages/wallet-webui/src/ui/error.ts
@@ -19,7 +19,28 @@ export function localError(message: string): ErrorPresentation {
   return { message };
 }
 
+function insufficientBalanceDetails(
+  detail: TalerErrorDetail,
+): PaymentInsufficientBalanceDetails | undefined {
+  return (
+    detail as {
+      insufficientBalanceDetails?: PaymentInsufficientBalanceDetails;
+    }
+  ).insufficientBalanceDetails;
+}
+
+/** A deposit refused because no exchange can wire to this kind of account. */
+function depositTargetUnsupported(detail: TalerErrorDetail): boolean {
+  return (
+    detail.code === TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE &&
+    insufficientBalanceDetails(detail)?.causeHint ===
+      InsufficientBalanceHint.MerchantDepositInsufficient
+  );
+}
+
 function errorMessage(detail: TalerErrorDetail, fallback: string): string {
+  if (depositTargetUnsupported(detail))
+    return /* Translators: A deposit sends digital cash from this wallet to an external account, usually a bank account; it does not add funds to the wallet. */ i18n.str`The exchanges of this wallet cannot deposit to this type of bank account.`;
   switch (detail.code) {
     case TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED:
       return /* Translators: Claiming a merchant order assigns it to a wallet for payment; it does not mean that payment has completed. */ i18n.str`This order was already claimed by another wallet.`;
@@ -28,13 +49,10 @@ function errorMessage(detail: TalerErrorDetail, fallback: string): string {
     case TalerErrorCode.WALLET_MERCHANT_ORDER_NOT_FOUND:
       return i18n.str`The merchant order no longer exists or has expired.`;
     case TalerErrorCode.WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE:
-      return insufficientBalanceMessage(
-        (
-          detail as {
-            insufficientBalanceDetails?: PaymentInsufficientBalanceDetails;
-          }
-        ).insufficientBalanceDetails,
-      );
+    case TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE:
+    case TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE:
+    case TalerErrorCode.WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE:
+      return insufficientBalanceMessage(insufficientBalanceDetails(detail));
     case TalerErrorCode.WALLET_NETWORK_ERROR:
       return i18n.str`The wallet could not reach the service.`;
     case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT:
@@ -79,6 +97,7 @@ function errorMessage(detail: TalerErrorDetail, fallback: string): string {
 }
 
 function errorGuidance(detail: TalerErrorDetail): string | undefined {
+  if (depositTargetUnsupported(detail)) return undefined;
   switch (detail.code) {
     case TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED:
       return i18n.str`Return to the merchant and request a new order.`;
@@ -87,6 +106,9 @@ function errorGuidance(detail: TalerErrorDetail): string | undefined {
     case TalerErrorCode.WALLET_MERCHANT_ORDER_NOT_FOUND:
       return i18n.str`Return to the merchant and create a new order.`;
     case TalerErrorCode.WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE:
+    case TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE:
+    case TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE:
+    case TalerErrorCode.WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE:
       return /* Translators: Withdrawing adds digital cash to this wallet, usually funded by a bank transfer; it does not cash out the wallet to a bank account. */ i18n.str`Withdraw funds or choose another payment option, then try again.`;
     case TalerErrorCode.WALLET_NETWORK_ERROR:
     case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT:
diff --git a/packages/wallet-webui/test/error.test.ts b/packages/wallet-webui/test/error.test.ts
index ec1288885..cc418b5b0 100644
--- a/packages/wallet-webui/test/error.test.ts
+++ b/packages/wallet-webui/test/error.test.ts
@@ -1,6 +1,11 @@
 import assert from "node:assert/strict";
 import test from "node:test";
-import { errorFromException } from "../src/ui/error.js";
+import {
+  AbsoluteTime,
+  InsufficientBalanceHint,
+  TalerErrorCode,
+} from "@gnu-taler/taler-util";
+import { errorFromException, walletCoreError } from "../src/ui/error.js";
 
 test("generic exceptions do not replace a translated user-facing fallback", () => {
   const error = errorFromException(
@@ -9,3 +14,49 @@ test("generic exceptions do not replace a translated user-facing fallback", () =
   );
   assert.equal(error.message, "Operation failed");
 });
+
+test("deposit and peer-payment shortfalls are explained like merchant payments", () => {
+  for (const code of [
+    TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE,
+    TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
+    TalerErrorCode.WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE,
+  ]) {
+    const error = walletCoreError(
+      {
+        code,
+        when: AbsoluteTime.now(),
+        insufficientBalanceDetails: {
+          amountRequested: "KUDOS:50",
+          balanceAvailable: "KUDOS:20",
+          causeHint: InsufficientBalanceHint.WalletBalanceAvailableInsufficient,
+        },
+      },
+      "Operation failed",
+    );
+    assert.equal(
+      error.message,
+      "The wallet has KUDOS:20 available, which is not enough for KUDOS:50.",
+    );
+    assert.ok(error.guidance);
+  }
+});
+
+test("a deposit to an account type no exchange serves is not called a shortfall", () => {
+  const error = walletCoreError(
+    {
+      code: TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE,
+      when: AbsoluteTime.now(),
+      insufficientBalanceDetails: {
+        amountRequested: "KUDOS:1",
+        balanceAvailable: "KUDOS:20",
+        causeHint: InsufficientBalanceHint.MerchantDepositInsufficient,
+      },
+    },
+    "Operation failed",
+  );
+  assert.equal(
+    error.message,
+    "The exchanges of this wallet cannot deposit to this type of bank account.",
+  );
+  assert.equal(error.guidance, undefined);
+});

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:33bot-cgNew Issue
2026-11-11 01:33bot-cgFile Added: before-web-1-deposit.png
2026-11-11 01:33bot-cgFile Added: before-web-2-deposit-details.png
2026-11-11 01:33bot-cgFile Added: before-web-3-paypull.png
2026-11-11 01:33bot-cgFile Added: before-web-4-deposit-iban.png
2026-11-11 01:33bot-cgFile Added: before-android-1-deposit.png
2026-11-11 01:33bot-cgFile Added: before-android-2-paypull.png
2026-11-11 01:33bot-cgFile Added: before-android-3-paypull-behind.png
2026-11-11 01:33bot-cgFile Added: before-ios-2-paypull.png
2026-11-11 01:33bot-cgFile Added: after-web-1-deposit.png
2026-11-11 01:33bot-cgFile Added: after-web-3-paypull.png
2026-11-11 01:33bot-cgFile Added: after-web-4-deposit-iban.png
2026-11-11 01:33bot-cgFile Added: web-insufficient-balance-wording.patch