{"record":{"id":"127ddc8f9cee6bea","repo":"tinyhumansai/openhuman","slug":"failed-to-derive-private-key-for-path-derivation","errorCode":null,"errorMessage":"Failed to derive private key for path ${derivationPath}","messagePattern":"Failed to derive private key for path (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/utils/cryptoKeys.ts","lineNumber":140,"sourceCode":"}\n\n/** Simple checksum: lowercase with 0x, then capitalize by hash. */\nfunction toChecksumAddress(address: string): string {\n  const a = address.replace(/^0x/i, '').toLowerCase();\n  const hash = bytesToHex(keccak_256(new TextEncoder().encode(a)));\n  let result = '0x';\n  for (let i = 0; i < 40; i++) {\n    result += parseInt(hash[i], 16) >= 8 ? a[i].toUpperCase() : a[i];\n  }\n  return result;\n}\n\nfunction deriveSecp256k1PrivateKey(mnemonic: string, derivationPath: string): Uint8Array {\n  const seed = mnemonicToSeedSync(mnemonic);\n  const hdkey = HDKey.fromMasterSeed(seed);\n  const derived = hdkey.derive(derivationPath);\n  if (!derived.privateKey) {\n    throw new Error(`Failed to derive private key for path ${derivationPath}`);\n  }\n  return derived.privateKey;\n}\n\nfunction deriveSlip10Ed25519PrivateKey(seed: Uint8Array, derivationPath: string): Uint8Array {\n  let key = hmac(sha512, new TextEncoder().encode('ed25519 seed'), seed);\n  let privateKey = key.slice(0, 32);\n  let chainCode = key.slice(32);\n\n  for (const segment of derivationPath.split('/').slice(1)) {\n    if (!segment.endsWith(\"'\")) {\n      throw new Error(`Ed25519 derivation path must be fully hardened: ${derivationPath}`);\n    }\n    const index = Number.parseInt(segment.slice(0, -1), 10);\n    const hardened = (index + 0x80000000) >>> 0;\n    const data = new Uint8Array(37);\n    data[0] = 0;\n    data.set(privateKey, 1);","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/utils/cryptoKeys.ts#L122-L158","documentation":"deriveSecp256k1PrivateKey() runs BIP32 (@scure/bip32 HDKey) over mnemonicToSeedSync(mnemonic) and calls hdkey.derive(derivationPath); if the returned node has no privateKey the function refuses to continue. A neutered/public-only node comes back when the path is malformed or the derivation cannot produce a private key for it.","triggerScenarios":"A derivation path that is not a valid BIP-32 path: not starting with 'm/', empty segments ('m//0'), unhardened index ≥ 2^31, garbage characters, or a path string the HDKey implementation silently degrades on; also a custom path with unquoted apostrophes mangled by shell/JSON escaping.","commonSituations":"User-editable derivation-path inputs in wallet settings; copying EVM/Solana paths between chains with wrong formatting; paths assembled by string concat that drop the leading m or the quotes (m/44'/60'/0'/0/0 typed as m/44/60/0/0/0 is fine for secp256k1 but mixed quoting breaks).","solutions":["Validate the path against ^m(\\/[0-9]+'?)+$ before deriving and reject early with a clear message","Use well-known per-chain constants (m/44'/60'/0'/0/i for EVM, m/44'/501'/0'/0' for Solana) instead of free-text input","Ensure apostrophes survive whatever stored/transported the path (h instead of ' also works: m/44h/60h/0h/0h/0)"],"exampleFix":"// before\nconst key = deriveSecp256k1PrivateKey(mnemonic, rawUserInput);\n// after\nif (!/^m(\\/[0-9]+'?)+$/.test(path)) throw new Error(`Invalid derivation path: ${path}`);\nconst key = deriveSecp256k1PrivateKey(mnemonic, path);","handlingStrategy":"validation","validationCode":"const BIP32_PATH = /^m(?:\\/[0-9]{1,10}'?)+$/;\nfunction assertBip32Path(path: string): void {\n  if (!BIP32_PATH.test(path)) throw new Error(`Invalid BIP-32 path: ${path}`);\n}","typeGuard":"function isBip32Path(path: string): boolean {\n  return BIP32_PATH.test(path);\n}","tryCatchPattern":null,"preventionTips":["Offer per-chain path presets instead of free-text derivation inputs","Accept both ' and h hardened notations, normalize before deriving","Round-trip paths through JSON so shell quoting cannot strip apostrophes"],"tags":["crypto","wallet","bip32","hd-key","derivation-path"],"backgroundTag":"hd-key-derivation-failed","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}