Repository navigation
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
What happened
The goal was "pay this invoice" (a 1 sat signet lntbs BOLT11). No profile or wallet was
given. To find a wallet, the agent had to:
organizations list --jsonreturned many organizations.wallets list --org ORG --jsonfor each organization, in a shell loop, withpython3
to flatten names, networks, and balances.- A second
python3pass read thebalances[].availableobjects.
The loop found two wallets, both in one organization. The BTC wallet showed
{"amount": N, "negative": true}, so its balance was negative and it could not pay.
The other was a USD wallet (see 2026-10-14-sub-cent-invoice-from-usd-wallet). Learning
that no wallet could pay a 1 sat invoice took roughly ten tool calls.
Why it was confusing
wallets listworks on one organization at a time. No command lists the wallets
across every organization that a login can see.- No command answers "which wallet can pay this invoice?" The network from the invoice
prefix (lntbsis signet or mutinynet), the currency, and a spendable balance all
matter. - A negative balance is a positive
amountwithnegative: true. A quick read of the
amount can show a funded wallet. - In non-interactive use, the wallet picker on
feat/cli-interactive-pickersdoes not
help. - Cheapest fixes, from smallest: show signed balances in table output; add
wallets list --all-orgs; givepayments send --invoicewithout--walletan error
that lists suitable wallets, or says that none can pay and why.
Provenance
- Found 2026-10-14 during a Claude Code session ("pay this invoice").
- Branch
feat/cli-human-output, commita060712,voltage 0.1.0 (a060712). - Deployment: staging, a login that sees many organizations, only one of which has wallets.
Status on other branches
feat/cli-interactive-pickersprompts for a missing organization, environment, or
wallet on a terminal. It does not help agents and does not filter by ability to pay.feat/cli-error-hintsnames the next command for missing scope. It does not search
across organizations.
Reproduction
repro.sh needs a browser login (voltage login --account me --credential-store file).
It shows the per-organization loop that an agent must write.
# docs/issues/2026-10-14-find-a-wallet-that-can-pay/repro.sh
#!/usr/bin/env bash
PIN=a060712
source "$(git rev-parse --show-toplevel)/docs/issues/lib.sh"
build
ACCOUNT=${ACCOUNT:-me}
# Today this needs a loop and a JSON parser; a fix makes it one voltage command.
orgs=$(voltage organizations list --account "$ACCOUNT" --json \
| python3 -c 'import json,sys; [print(o["id"]) for o in json.load(sys.stdin)["data"]]')
for org in $orgs; do
voltage wallets list --account "$ACCOUNT" --org "$org" --json 2>/dev/null | python3 -c '
import json, sys
for w in json.load(sys.stdin).get("data") or []:
for b in w["balances"]:
a = b["available"]
print(sys.argv[1], w["id"], w["network"], b["currency"], ("-" if a["negative"] else "") + str(a["amount"]), a["unit"])
' "$org" || true
donedocs/issues/lib.sh
#!/usr/bin/env bash
# Shared setup for issue reproductions. Source it from a repro.sh after setting PIN.
set -euo pipefail
root=$(git rev-parse --show-toplevel)
cd "$root"
head=$(git rev-parse --short HEAD)
if [ "${PIN:-}" != "" ] && [ "$head" != "$PIN" ]; then
echo "note: HEAD is $head; the issue was found at $PIN (git switch --detach $PIN to match)" >&2
fi
voltage() { "$root/scripts/voltage-staging" "$@"; }
need() { for v in "$@"; do [ -n "${!v:-}" ] || { echo "set $v first" >&2; exit 64; }; done; }
build() { "$root/scripts/voltage-staging" --build >/dev/null 2>&1; }
# Run a command, print its exit code, and never stop the script.
try() { echo "\$ voltage $*" >&2; set +e; voltage "$@"; echo "exit=$?" >&2; set -e; }Run with nix develop --command ./docs/issues/2026-10-14-find-a-wallet-that-can-pay/repro.sh (see the agent friction log in AGENTS.md, #33).
Progress on master (b25c05d, 2026-10-20)
Done:
- Table output shows a negative balance with a
-sign (src/output/human.rs). feat(cli): fit human tables to the terminal and scrub all API text #15 - On a terminal, a payment without
--walletoffers a wallet picker. feat(cli): pick a missing organization, environment, or wallet #22 - A payment without
--currencytakes its wallet's currency. feat(cli): default a payment's currency to its wallet's, and units to base units #55
Left:
- List wallets across every organization a login can see, for example
wallets list --all-orgs. - Find a wallet that can pay a given invoice, from its network, currency, and spendable balance.
- Off a terminal,
payments send --invoicewithout--walletlists suitable wallets, or says that none can pay and why. - JSON still reports a negative balance as a positive
amountwithnegative: true.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
tee8z commentedon Oct 20, 2026
Partly helped:
--walletis missing.--currencyis omitted.Still open: nothing lists wallets across organizations (
wallets listtakes one--org), no command finds a wallet that can pay a given invoice, and a send without--walletoff a terminal does not suggest one.thebrandonlucas commentedon Oct 21, 2026
Plan (2026-10-21):
payments send --invoicewithout--wallet: done in the CLI. Without--wallet, the error lists the wallets in scope that match the invoice network (from its prefix) and currency and have a positive balance, or says why none qualify. The list is worded as a shortlist, not a guarantee, because only the backend knows fees, limits and line-of-credit rules.--all-orgs): on hold, waiting for voltagecloud/backend#2296. Each organization needs its own token exchange today, so the CLI would make 2N + 1 calls, and only with a browser login.amount+negative: true): closed by decision. This is the API shape, which the CLI passes through, and changing it would break--jsonconsumers. Human tables already show the-sign.thebrandonlucas commentedon Oct 21, 2026
Update (2026-10-21): voltagecloud/backend#2296 is closed by decision. The backend will not add a cross-organization wallet endpoint, because most users belong to one organization.
--all-orgsmoves back to the CLI. It sends the per-organization token exchange andGET /organizations/{id}/walletsrequests in parallel and merges the results. This works for browser logins only, because an API key is bound to one organization. A related follow-up: make a profile that pins an organization the stronger default, so that most commands never need a cross-organization lookup.feat(cli): name the wallets that could pay a BOLT11 invoice
feat(cli): list wallets in every organization with --all-orgs
feat(cli): name the wallets that could pay a BOLT11 invoice (#73)
fix(cli): name each candidate's environment when --env is not given (#77
feat(cli): list wallets in every organization with --all-orgs (#74)