louislam/uptime-kuma · error · Error
Failed to decode private key for sender ${sender}: ${error.m
Error message
Failed to decode private key for sender ${sender}: ${error.message} What it means
Thrown by Nostr.getPrivateKey when nip19.decode(sender) fails. The sender is expected to be a bech32-encoded private key (nsec1...). If decode rejects (invalid bech32, wrong prefix, malformed string), the provider rethrows as 'Failed to decode private key for sender <sender>: <inner.message>'. The returned data is then used directly as the private key for signing/sealing.
Source
Thrown at server/notification-providers/nostr.js:86
// Report success or failure
if (successfulRelays === 0) {
throw Error("Failed to connect to any relays.");
}
return `${successfulRelays}/${relays.length} relays connected.`;
}
/**
* Get the private key for the sender
* @param {string} sender Sender to retrieve key for
* @returns {nip19.DecodeResult} Private key
*/
async getPrivateKey(sender) {
try {
const senderDecodeResult = await nip19.decode(sender);
const { data } = senderDecodeResult;
return data;
} catch (error) {
throw new Error(`Failed to decode private key for sender ${sender}: ${error.message}`);
}
}
/**
* 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 {View on GitHub (pinned to 6b5ea01557)
Solutions
- Generate or obtain the sender's private key in nsec form (nsec1...) from your Nostr client and paste that.
- Trim whitespace/newlines from notification.sender before saving.
- Do not use an npub here — npub is public-only and cannot sign.
- Verify the nsec decodes in isolation with nip19.decode to confirm it is well-formed.
Example fix
// before
const senderDecodeResult = await nip19.decode(sender);
// after - normalize input and give a clearer failure
const trimmed = String(sender || "").trim();
if (!trimmed.startsWith("nsec1")) {
throw new Error(`Sender must be an nsec private key, got: ${trimmed.slice(0, 10)}...`);
}
const senderDecodeResult = await nip19.decode(trimmed); Defensive patterns
Strategy: validation
Validate before calling
const sender = String(notification.sender || "").trim();
if (!sender.startsWith("nsec1")) {
throw new Error("Sender must be an nsec1... private key");
} Type guard
/** @param {string} s */
function looksLikeNsec(s) {
return typeof s === "string" && s.startsWith("nsec1") && s.length > 10;
} Try / catch
try {
return (await nip19.decode(sender)).data;
} catch (error) {
throw new Error(`Sender is not a valid nsec private key: ${error.message}`);
} Prevention
- Trim whitespace from the sender field before storing.
- Reject non-nsec prefixes at save time.
- Never store or log full private keys in plaintext beyond the config.
When it happens
Trigger: notification.sender is empty, is a raw hex string (not nsec-prefixed), is an npub (public key), has a typo breaking bech32 checksum, or is an nsec from an incompatible nostr-tools version.
Common situations: User pasted a public key (npub) into the sender/private-key field; pasted a hex key; copied an nsec with leading/trailing whitespace or a missing character that breaks the checksum.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Recipient ${recipient} is not an npub
- Failed to create gift-wrapped event for recipient: ${error.m
- Error decoding recipient ${recipient}: ${error}
- Invalid Docker response, is it Docker really a daemon?
- domain_expiry_unsupported_monitor_type
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/7dadb136fd8e2045.
Report an issue: GitHub.