View Issue Details

IDProjectCategoryView StatusLast Update
0012114Talerwallet-corepublic2026-11-11 04:16
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 
Summary0012114: initiatePeerPushDebit with a zero amount throws (7001, stack trace) instead of answering 7048 like checkPeerPushDebitV2 -- the positive-amount guard has one caller
Description`initiatePeerPushDebit` with an amount of zero does not return an error --
it throws. The request reaches coin selection, which raises a plain `Error`,
and the caller gets WALLET_UNEXPECTED_EXCEPTION (7001) with a JavaScript
stack trace:

    initiatePeerPushDebit{"restrictScope":{...KUDOS...},
        "partialContractTerms":{"summary":"harness",
        "purse_expiration":{"t_s":1794370160},"amount":"KUDOS:0"}}

    finished wallet core request initiatePeerPushDebit with error: {
      "code": 7001,
      "hint": "unexpected exception (message: coin selection for
               peer-to-peer payment with amount of zero not supported)",
      "stack": " at selectPeerCoinsInTx (<compiled_js>:55227:20)\n ..." }

The sibling request already handles this case properly. Ten seconds earlier
in the same session, same amount:

    checkPeerPushDebitV2{"amount":"KUDOS:0",...}
    -> { "code": 7048, "hint": "peer push payment amount must be positive",
         "parameter": "amount" }

Cause
-----

packages/taler-wallet-core/src/pay-peer-push-debit.ts has the guard as a
function of its own,

    export function requirePositivePeerPushDebitAmount(amount: AmountJson): void

and on master (checked 2026-11-11) it has exactly one caller, line 628, in
`checkPeerPushDebitV2`. `initiatePeerPushDebit` (line 1471) parses the
amount and goes straight on, so zero arrives at
coinSelection.ts:2535,

    if (Amounts.isZero(instructedAmount)) {
      // Other parts of the code assume that we have at least
      // one coin to spend.
      throw new Error("coin selection for peer-to-peer payment with amount of zero not supported");

which is an assertion, not an API answer. A client that skips the check, or
ignores its result, turns a bad parameter into an "internal error".

Fix (patch attached, initiate-peer-push-zero-amount.diff): call
`requirePositivePeerPushDebitAmount(instructedAmount)` at the top of
`initiatePeerPushDebit`, so both entry points give the same 7048. Four
lines. The diff is against master and has not been compiled or run by me;
it calls a function already defined and exported in the same file.

How it was reached (iOS wallet)
-------------------------------

Found by a UI fuzzing walk (c2h-21-e00044, 2026-11-11, no fault injected),
in five taps from a wallet holding KUDOS 25:

    Actions -> Send -> Next (amount left at 0.00) -> type a subject
            -> "Send KUDOS 0.00 now" -> "Internal core error" + the JSON
               above, stack trace included

Two things on the iOS side let it through, which may deserve their own
decision rather than a fix here:

  * `AmountInputV.swift:71` -- `let isZero = amountToTransfer.isZero &&
    !developerMode` -- so with Developer Mode on, Next is enabled at 0.00.
    That looks deliberate. With Developer Mode off the path is closed.
  * the subject screen then offers "Send KUDOS 0.00 now" although
    `checkPeerPushDebitV2` has just failed with 7048; the failed check does
    not disable the button.

So the user-visible defect needs Developer Mode; the wallet-core defect
does not need the iOS app at all.

Severity: minor. No funds move and nothing is persisted (the request fails
in 18 ms, before a transaction exists). The cost is an unexpected-exception
report, with a stack trace, for what is an ordinary invalid parameter.
TagsNo tags attached.
Attached Files
12114-initiate-peer-push-zero-amount.diff (699 bytes)   
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts	2026-11-11 04:14:57.378485375 +0100
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts	2026-11-11 04:14:57.382485348 +0100
@@ -1475,6 +1475,10 @@
   const instructedAmount = Amounts.parseOrThrow(
     req.partialContractTerms.amount,
   );
+  // Same guard as checkPeerPushDebitV2: without it a zero amount reaches
+  // selectPeerCoinsInTx, which throws a plain Error and surfaces as
+  // WALLET_UNEXPECTED_EXCEPTION with a stack trace.
+  requirePositivePeerPushDebitAmount(instructedAmount);
   const currency = Amounts.currencyOf(instructedAmount);
 
   const requestedExpiration = req.partialContractTerms.purse_expiration;

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

bot-cg [agent]

2026-11-11 04:16

updater   ~0030404

Reproduced as a deterministic UI test (harness scenario send-zero-devmode, 2026-11-11, no fault): fund with demo cash, Actions -> Send -> Next -> subject -> "Send KUDOS 0.00 now". Both log oracles held -- initiatePeerPushDebit was sent with KUDOS:0 and answered with the unexpected exception. Screenshot of the result attached (12114-send-zero-internal-core-error.png): the stack trace is on the user's screen.

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 03:25bot-cgNew Issue
2026-11-11 03:25bot-cgFile Added: 12114-initiate-peer-push-zero-amount.diff
2026-11-11 04:16bot-cgFile Added: 12114-send-zero-internal-core-error.png
2026-11-11 04:16bot-cgNote Added: 0030404