tinyhumansai/openhuman · error
Failed to derive private key for path
Error message
Failed to derive private key for path ${derivationPath} What it means
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.
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)
Example fix
// before
const key = deriveSecp256k1PrivateKey(mnemonic, rawUserInput);
// after
if (!/^m(\/[0-9]+'?)+$/.test(path)) throw new Error(`Invalid derivation path: ${path}`);
const key = deriveSecp256k1PrivateKey(mnemonic, path); Defensive patterns
Strategy: validation
Validate before calling
const BIP32_PATH = /^m(?:\/[0-9]{1,10}'?)+$/;
function assertBip32Path(path: string): void {
if (!BIP32_PATH.test(path)) throw new Error(`Invalid BIP-32 path: ${path}`);
} Type guard
function isBip32Path(path: string): boolean {
return BIP32_PATH.test(path);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- Ed25519 derivation path must be fully hardened
- encrypted payload too short
- encryption key must decode to 32 raw bytes
- Failed to secure recovery phrase. Please try again.
- Recovery phrase is required.
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/127ddc8f9cee6bea.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/utils/cryptoKeys.ts:140
}
/** Simple checksum: lowercase with 0x, then capitalize by hash. */
function toChecksumAddress(address: string): string {
const a = address.replace(/^0x/i, '').toLowerCase();
const hash = bytesToHex(keccak_256(new TextEncoder().encode(a)));
let result = '0x';
for (let i = 0; i < 40; i++) {
result += parseInt(hash[i], 16) >= 8 ? a[i].toUpperCase() : a[i];
}
return result;
}
function deriveSecp256k1PrivateKey(mnemonic: string, derivationPath: string): Uint8Array {
const seed = mnemonicToSeedSync(mnemonic);
const hdkey = HDKey.fromMasterSeed(seed);
const derived = hdkey.derive(derivationPath);
if (!derived.privateKey) {
throw new Error(`Failed to derive private key for path ${derivationPath}`);
}
return derived.privateKey;
}
function deriveSlip10Ed25519PrivateKey(seed: Uint8Array, derivationPath: string): Uint8Array {
let key = hmac(sha512, new TextEncoder().encode('ed25519 seed'), seed);
let privateKey = key.slice(0, 32);
let chainCode = key.slice(32);
for (const segment of derivationPath.split('/').slice(1)) {
if (!segment.endsWith("'")) {
throw new Error(`Ed25519 derivation path must be fully hardened: ${derivationPath}`);
}
const index = Number.parseInt(segment.slice(0, -1), 10);
const hardened = (index + 0x80000000) >>> 0;
const data = new Uint8Array(37);
data[0] = 0;
data.set(privateKey, 1);View on GitHub (pinned to 7491200858)