Is there an existing issue for this?
Describe the bug
worker/nostr.js (nip57) extracts the relay list from the zap request like this:
const relays = note.tags.find(t => t?.length >= 2 && t[0] === 'relays').slice(1)
If the zap request note has no relays tag, find returns undefined and .slice(1) throws TypeError: Cannot read properties of undefined (reading 'slice'). The throw is inside the try/catch, so it is swallowed as a logged failed to publish NIP-57 receipt: error and the zap receipt (kind 9735) is never published — even though the payer already paid the invoice.
The problem is that such a note can reach the worker: the NIP-57 validation in pages/api/lnurlp/[username]/pay.js only checks the signature, the p tag count, the e tag count and the optional amount tag — it does not require a relays tag. NIP-57 Appendix D also phrases it loosely ("There should be a relays tag"), so third-party wallets are allowed to omit it. The invoice is created with the zap request's description hash, the payment settles, nip57 is queued, and the receipt is silently lost.
Minimal repro of the crash (node):
const note = { pubkey: 'ab'.repeat(32), tags: [['p','cd'.repeat(32)],['amount','21000']] }
note.tags.find(t => t?.length >= 2 && t[0] === 'relays').slice(1)
// TypeError: Cannot read properties of undefined (reading 'slice')
This affects both the proxied path (payInBolt11.nostrNote) and the new direct-receive path (externalTransaction.nostrNote) added in ca3b05e.
Steps To Reproduce
- Send a zap to a
user@stacker.news lightning address with a signed NIP-57 zap request that has valid p/amount tags but no relays tag (some wallets/clients omit it).
pay.js accepts the note (validation passes), returns a description-hash invoice.
- Pay the invoice.
- The
nip57 boss job runs and throws on the relay extraction; the receipt is never published and the zap sender's client never sees a zap receipt for a payment that succeeded.
Expected behavior
Either:
- reject zap requests without a
relays tag during validation in pay.js (fail fast with invalid NIP-57 note, so the payer knows before paying), or
- make the worker defensive, e.g.
const relays = note.tags.find(...)?.slice(1) ?? DEFAULT_CROSSPOSTING_RELAYS so the receipt still gets published to sensible default relays.
Logs
failed to publish NIP-57 receipt: TypeError: Cannot read properties of undefined (reading 'slice')
Device information
Additional context
Found while reading the wallet/nostr changes from ca3b05e ("wallets: support configurable direct and proxied receives"). I can submit a small draft PR with either fix if the maintainers prefer one direction.
Is there an existing issue for this?
Describe the bug
worker/nostr.js(nip57) extracts the relay list from the zap request like this:If the zap request note has no
relaystag,findreturnsundefinedand.slice(1)throwsTypeError: Cannot read properties of undefined (reading 'slice'). The throw is inside the try/catch, so it is swallowed as a loggedfailed to publish NIP-57 receipt:error and the zap receipt (kind 9735) is never published — even though the payer already paid the invoice.The problem is that such a note can reach the worker: the NIP-57 validation in
pages/api/lnurlp/[username]/pay.jsonly checks the signature, theptag count, theetag count and the optionalamounttag — it does not require arelaystag. NIP-57 Appendix D also phrases it loosely ("There should be arelaystag"), so third-party wallets are allowed to omit it. The invoice is created with the zap request's description hash, the payment settles,nip57is queued, and the receipt is silently lost.Minimal repro of the crash (node):
This affects both the proxied path (
payInBolt11.nostrNote) and the new direct-receive path (externalTransaction.nostrNote) added in ca3b05e.Steps To Reproduce
user@stacker.newslightning address with a signed NIP-57 zap request that has validp/amounttags but norelaystag (some wallets/clients omit it).pay.jsaccepts the note (validation passes), returns a description-hash invoice.nip57boss job runs and throws on the relay extraction; the receipt is never published and the zap sender's client never sees a zap receipt for a payment that succeeded.Expected behavior
Either:
relaystag during validation inpay.js(fail fast withinvalid NIP-57 note, so the payer knows before paying), orconst relays = note.tags.find(...)?.slice(1) ?? DEFAULT_CROSSPOSTING_RELAYSso the receipt still gets published to sensible default relays.Logs
Device information
Additional context
Found while reading the wallet/nostr changes from ca3b05e ("wallets: support configurable direct and proxied receives"). I can submit a small draft PR with either fix if the maintainers prefer one direction.