You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
54 such events; the last 40 retries are 37× Inactive Keyset + 3× transport failure. Three delivered-and-unpaid jobs are stuck behind this, and the seat's own 423 sats at that mint are unspendable through the normal path.
The seat is otherwise fine — it kept trading at the other accepted mint (6 jobs, 851 sats, all clean).
The mechanism
Inactive Keyset is raised by cdk on outputs, not inputs:
// cdk-0.17.2/src/mint/verification.rs:72-77if !keyset.active{
tracing::debug!("Transaction attempted with inactive keyset in outputs: {}", …);returnErr(Error::InactiveKeyset);}
Spending old proofs is legal — that is what rotation is for. What the mint refuses is being asked to sign new outputs under a keyset it has retired. So the wallet is constructing blinded outputs against a stale cached keyset and retrying that same doomed request every ~5 minutes, forever.
Manually proven on the seat that the underlying swap is fine:
The mint accepted the old inputs and issued a token. So there IS an active keyset and the funds are recoverable — the automatic path just never asks for it.
Why it never self-heals
cdk already provides the machinery — refresh_keysets(), fetch_active_keyset(), get_active_keyset() (cdk-0.17.2/src/wallet/wallet_trait.rs:81-102).
maxplayer calls it in exactly one place, and it is not the redeem path:
That is fee calculation. The receive/redeem path itself never refreshes, so a rotation mid-flight is unrecoverable without operator intervention.
Suggested fix
On InactiveKeyset from a receive/redeem, call refresh_keysets() and retry once against the now-active keyset, rather than re-queueing the identical request. The error is specific and unambiguous, so this does not need heuristics — and cdk already exposes everything required.
Worth also surfacing it to the operator: today the only signal is a repeating log line that reads like a generic wallet failure.
Please be surgical about WHEN keysets are fetched
The fix is error-triggered, not periodic. Do not turn this into a keyset poll, a refresh-before-every-operation, or a startup-wide prefetch:
Fetch only when the mint has actually told us the cached keyset is stale — i.e. on an InactiveKeyset error. That is the one moment we know a refresh is warranted. Rotation is rare; every other request should keep using the cached keyset.
One refresh + one retry per failure, then give up and surface it. No refresh loops — if the retry also fails, that is a real error and should be reported, not hidden behind more fetching.
Don't refresh on unrelated failures. Transport errors and 5xx are downtime, not rotation; refreshing on those adds network round-trips to a mint that is already struggling, on exactly the path that is failing. (This seat saw both error classes interleaved from the same mint — 37× Inactive Keyset and 3× transport failure — so the distinction is not hypothetical.)
The goal is a wallet that reacts correctly to a keyset rotation, not one that keeps checking whether a rotation has happened.
Priority
Not beta-blocking (gudnuf's call) — the seat keeps trading at other mints and the funds are recoverable manually. Filing so it is not lost: it is a silent money-stuck path that a friend running a seat would experience as "payments just never arrived."
What happened (field-observed, ember seat, 2026-08-11)
minibits went down (HTTP 502) and came back having rotated its keysets. Since
10:38:37Zevery buffered payment receive on the seat fails with:54 such events; the last 40 retries are 37×
Inactive Keyset+ 3× transport failure. Three delivered-and-unpaid jobs are stuck behind this, and the seat's own 423 sats at that mint are unspendable through the normal path.The seat is otherwise fine — it kept trading at the other accepted mint (6 jobs, 851 sats, all clean).
The mechanism
Inactive Keysetis raised by cdk on outputs, not inputs:Spending old proofs is legal — that is what rotation is for. What the mint refuses is being asked to sign new outputs under a keyset it has retired. So the wallet is constructing blinded outputs against a stale cached keyset and retrying that same doomed request every ~5 minutes, forever.
Manually proven on the seat that the underlying swap is fine:
The mint accepted the old inputs and issued a token. So there IS an active keyset and the funds are recoverable — the automatic path just never asks for it.
Why it never self-heals
cdk already provides the machinery —
refresh_keysets(),fetch_active_keyset(),get_active_keyset()(cdk-0.17.2/src/wallet/wallet_trait.rs:81-102).maxplayer calls it in exactly one place, and it is not the redeem path:
That is fee calculation. The receive/redeem path itself never refreshes, so a rotation mid-flight is unrecoverable without operator intervention.
Suggested fix
On
InactiveKeysetfrom a receive/redeem, callrefresh_keysets()and retry once against the now-active keyset, rather than re-queueing the identical request. The error is specific and unambiguous, so this does not need heuristics — and cdk already exposes everything required.Worth also surfacing it to the operator: today the only signal is a repeating log line that reads like a generic wallet failure.
Please be surgical about WHEN keysets are fetched
The fix is error-triggered, not periodic. Do not turn this into a keyset poll, a refresh-before-every-operation, or a startup-wide prefetch:
InactiveKeyseterror. That is the one moment we know a refresh is warranted. Rotation is rare; every other request should keep using the cached keyset.Inactive Keysetand 3× transport failure — so the distinction is not hypothetical.)The goal is a wallet that reacts correctly to a keyset rotation, not one that keeps checking whether a rotation has happened.
Priority
Not beta-blocking (gudnuf's call) — the seat keeps trading at other mints and the funds are recoverable manually. Filing so it is not lost: it is a silent money-stuck path that a friend running a seat would experience as "payments just never arrived."
Environment
v0.2.1, macOS/seatbelt seatember=0.17.2https://mint.minibits.cash/Bitcoin