{"record":{"id":"7dadb136fd8e2045","repo":"louislam/uptime-kuma","slug":"failed-to-decode-private-key-for-sender-sender","errorCode":null,"errorMessage":"Failed to decode private key for sender ${sender}: ${error.message}","messagePattern":"Failed to decode private key for sender (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/notification-providers/nostr.js","lineNumber":86,"sourceCode":"        // Report success or failure\n        if (successfulRelays === 0) {\n            throw Error(\"Failed to connect to any relays.\");\n        }\n        return `${successfulRelays}/${relays.length} relays connected.`;\n    }\n\n    /**\n     * Get the private key for the sender\n     * @param {string} sender Sender to retrieve key for\n     * @returns {nip19.DecodeResult} Private key\n     */\n    async getPrivateKey(sender) {\n        try {\n            const senderDecodeResult = await nip19.decode(sender);\n            const { data } = senderDecodeResult;\n            return data;\n        } catch (error) {\n            throw new Error(`Failed to decode private key for sender ${sender}: ${error.message}`);\n        }\n    }\n\n    /**\n     * Get public keys for recipients\n     * @param {string} recipients Newline delimited list of recipients\n     * @returns {Promise<nip19.DecodeResult[]>} Public keys\n     */\n    async getPublicKeys(recipients) {\n        const recipientsList = recipients.split(\"\\n\");\n        const publicKeys = [];\n        for (const recipient of recipientsList) {\n            try {\n                const recipientDecodeResult = await nip19.decode(recipient);\n                const { type, data } = recipientDecodeResult;\n                if (type === \"npub\") {\n                    publicKeys.push(data);\n                } else {","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/notification-providers/nostr.js#L68-L104","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst senderDecodeResult = await nip19.decode(sender);\n\n// after - normalize input and give a clearer failure\nconst trimmed = String(sender || \"\").trim();\nif (!trimmed.startsWith(\"nsec1\")) {\n    throw new Error(`Sender must be an nsec private key, got: ${trimmed.slice(0, 10)}...`);\n}\nconst senderDecodeResult = await nip19.decode(trimmed);","handlingStrategy":"validation","validationCode":"const sender = String(notification.sender || \"\").trim();\nif (!sender.startsWith(\"nsec1\")) {\n    throw new Error(\"Sender must be an nsec1... private key\");\n}","typeGuard":"/** @param {string} s */\nfunction looksLikeNsec(s) {\n    return typeof s === \"string\" && s.startsWith(\"nsec1\") && s.length > 10;\n}","tryCatchPattern":"try {\n    return (await nip19.decode(sender)).data;\n} catch (error) {\n    throw new Error(`Sender is not a valid nsec private key: ${error.message}`);\n}","preventionTips":["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."],"tags":["nostr","crypto","nip19","key-decode","validation"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}