louislam/uptime-kuma · error · Error

Error decoding recipient ${recipient}: ${error}

Error message

Error decoding recipient ${recipient}: ${error}

What it means

The outer catch in Nostr.getPublicKeys wraps any per-recipient failure (a thrown decode error from nip19.decode, or the inner 'is not an npub' throw) as 'Error decoding recipient <recipient>: <error>'. Because it interpolates the whole error object (not error.message), the surfaced text may include the Error's string representation, sometimes twice-nested.

Source

Thrown at server/notification-providers/nostr.js:108

    /**
     * Get public keys for recipients
     * @param {string} recipients Newline delimited list of recipients
     * @returns {Promise<nip19.DecodeResult[]>} Public keys
     */
    async getPublicKeys(recipients) {
        const recipientsList = recipients.split("\n");
        const publicKeys = [];
        for (const recipient of recipientsList) {
            try {
                const recipientDecodeResult = await nip19.decode(recipient);
                const { type, data } = recipientDecodeResult;
                if (type === "npub") {
                    publicKeys.push(data);
                } else {
                    throw new Error(`Recipient ${recipient} is not an npub`);
                }
            } catch (error) {
                throw new Error(`Error decoding recipient ${recipient}: ${error}`);
            }
        }
        return publicKeys;
    }
}

module.exports = Nostr;

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Inspect the recipient token named in the message and fix or remove it.
  2. Pre-filter the recipient list: split on newline, trim, drop empty lines before decoding.
  3. Interpolate error.message instead of error to keep the surfaced message concise and non-redundant.
  4. Validate each entry starts with 'npub1' before attempting nip19.decode.

Example fix

// before
} catch (error) {
    throw new Error(`Error decoding recipient ${recipient}: ${error}`);
}

// after - cleaner message and skip blanks upstream
} catch (error) {
    throw new Error(`Error decoding recipient ${recipient}: ${error.message}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const recipientsList = recipients.split("\n").map(s => s.trim()).filter(Boolean);

Try / catch

for (const recipient of recipientsList) {
    try {
        const { type, data } = await nip19.decode(recipient);
        if (type !== "npub") throw new Error(`not an npub (got ${type})`);
        publicKeys.push(data);
    } catch (error) {
        throw new Error(`Cannot decode recipient '${recipient}': ${error.message}`);
    }
}

Prevention

When it happens

Trigger: Any recipient line that fails nip19.decode (invalid bech32, bad checksum, unrecognised prefix) or that decodes to a non-npub type. The inner throw at line 105 also lands here.

Common situations: A blank/whitespace line in the recipients textarea; a recipient copied with extra characters; an npub from a different bech32 convention; line-ending artifacts from copy/paste (CRLF).

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/b00ca5b40f114dab. Report an issue: GitHub.