taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 1776039b6f6cb9c896810f01d37b63ddc7b6cbf0
parent 5f00bcf20b16940f6761a9ec58117ea7ff665ada
Author: Florian Dold <dold@taler.net>
Date:   Thu,  3 Sep 2026 15:47:37 +0200

wallet-core: skip coins without a denomination in availability

Such coins exist after a denomination was retired or cleaned up;
throwing on them made every wallet start fail.

Diffstat:
Mpackages/taler-wallet-core/src/wallet.ts | 21++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -418,7 +418,7 @@ async function getOrCreateCoinAvailabilityForRematerialization( availabilities: WalletCoinAvailability[], byRef: Map<string, WalletCoinAvailability>, ref: WalletCoinAvailabilityRef, -): Promise<WalletCoinAvailability> { +): Promise<WalletCoinAvailability | undefined> { const key = coinAvailabilityRefKey(ref); const existing = byRef.get(key); if (existing) { @@ -426,10 +426,14 @@ async function getOrCreateCoinAvailabilityForRematerialization( } const denom = await tx.getDenomination(ref); if (!denom) { - throw Error( + // A coin can outlive its denomination record (the exchange retired it, + // or a cleanup removed it). It is not spendable without one, so it is + // not counted; the rebuild must not fail on it. + logger.warn( `denomination ${ref.denomPubHash} is missing while rebuilding` + - ` coin availability for age ${ref.maxAge}`, + ` coin availability for age ${ref.maxAge}, skipping`, ); + return undefined; } const availability: WalletCoinAvailability = { maxAge: ref.maxAge, @@ -482,6 +486,9 @@ export async function recomputeCoinAvailability( byRef, coin, ); + if (!availability) { + continue; + } availability.freshCoinCount++; if (coin.visible) { availability.visibleCoinCount++; @@ -509,9 +516,10 @@ export async function recomputeCoinAvailability( const oldCoinPub = group.oldCoinPubs[session.coinIndex]; const oldCoin = coinsByPub.get(oldCoinPub); if (!oldCoin) { - throw Error( - `old coin ${oldCoinPub} missing for refresh ${session.refreshGroupId}`, + logger.warn( + `old coin ${oldCoinPub} missing for refresh ${session.refreshGroupId}, skipping`, ); + continue; } for (const output of session.newDenoms) { const availability = @@ -525,6 +533,9 @@ export async function recomputeCoinAvailability( maxAge: oldCoin.maxAge, }, ); + if (!availability) { + continue; + } availability.pendingRefreshOutputCount = (availability.pendingRefreshOutputCount ?? 0) + output.count; }