{"record":{"id":"5bd1ee1570a1b081","repo":"tinyhumansai/openhuman","slug":"failed-to-secure-recovery-phrase-please-try-again","errorCode":null,"errorMessage":"Failed to secure recovery phrase. Please try again.","messagePattern":"Failed to secure recovery phrase\\. Please try again\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/features/wallet/setupLocalWalletFromMnemonic.ts","lineNumber":28,"sourceCode":"  mnemonic: string;\n  source: WalletSetupSource;\n  setEncryptionKey: (value: string | null) => Promise<void>;\n  /**\n   * Set to `true` only when the user has explicitly confirmed wallet replacement\n   * through the double-confirmation dialog. Defaults to `false`.\n   */\n  force?: boolean;\n}): Promise<void> {\n  const { mnemonic, source, setEncryptionKey, force } = args;\n  const words = mnemonic.trim().split(/\\s+/).filter(Boolean);\n  if (words.length === 0) {\n    throw new Error('Recovery phrase is required.');\n  }\n  const normalizedMnemonic = words.join(' ');\n  const aesKey = deriveAesKeyFromMnemonic(normalizedMnemonic);\n  const encryptedMnemonic = (await openhumanEncryptSecret(normalizedMnemonic)).result?.trim();\n  if (!encryptedMnemonic) {\n    throw new Error('Failed to secure recovery phrase. Please try again.');\n  }\n\n  await setEncryptionKey(aesKey);\n  await setupLocalWallet({\n    consentGranted: true,\n    source,\n    mnemonicWordCount: words.length,\n    encryptedMnemonic,\n    accounts: deriveWalletAccountsFromMnemonic(normalizedMnemonic),\n    force,\n  });\n}\n","sourceCodeStart":10,"sourceCodeEnd":41,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/features/wallet/setupLocalWalletFromMnemonic.ts#L10-L41","documentation":"Later in setupLocalWalletFromMnemonic(): the phrase was accepted and the renderer asks the core to encrypt it via openhumanEncryptSecret(normalizedMnemonic). The call returns an object whose result should be a trimmed non-empty ciphertext; if it comes back empty/undefined the flow throws 'Failed to secure recovery phrase. Please try again.' — i.e. the core-side encryption step failed silently.","triggerScenarios":"The openhumanEncryptSecret RPC resolves but with an empty result: core keyring/encryption unavailable (OS keychain locked or denied), an error payload swallowed by the RPC wrapper, or a core version whose encrypt-secret response shape differs (result field renamed) so .result?.trim() yields undefined.","commonSituations":"OS keychain access denied (macOS Keychain, Windows Credential Manager, Linux secret service absent in minimal WMs) so the core cannot create the encryption key; core/renderer version skew after a partial update; headless/Linux environments without a secrets service where the keyring backend fails.","solutions":["Retry once — transient keychain locks (especially right after login on macOS) can clear; the message itself says 'Please try again'.","Check OS keychain/secret-service health: on Linux ensure gnome-keyring or kwallet is running; on macOS unlock the login keychain; on Windows check Credential Manager permissions.","Verify core and app versions match (partial updates can skew RPC response shapes) — update or restart both.","Inspect core logs for the encrypt-secret handler's error to find the underlying keyring failure; enable the keyring consent flow if prompted."],"exampleFix":"// before\nconst encryptedMnemonic = (await openhumanEncryptSecret(normalizedMnemonic)).result?.trim();\nif (!encryptedMnemonic) throw new Error('Failed to secure recovery phrase. Please try again.');\n\n// after — retry once before failing\nlet encryptedMnemonic: string | undefined;\nfor (let attempt = 0; attempt < 2 && !encryptedMnemonic; attempt++) {\n  encryptedMnemonic = (await openhumanEncryptSecret(normalizedMnemonic)).result?.trim();\n}\nif (!encryptedMnemonic) throw new Error('Failed to secure recovery phrase. Please try again.');","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await setupLocalWalletFromMnemonic(args);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Failed to secure recovery phrase')) {\n    // keychain may be transiently locked — prompt retry once\n    if (await askUserToRetry()) return setupLocalWalletFromMnemonic(args);\n  }\n  throw err;\n}","preventionTips":["Ensure the OS keyring/secret service is available before starting wallet setup (surface a preflight warning on minimal Linux).","Keep core and app versions matched to avoid RPC response-shape skew.","Log the raw openhumanEncryptSecret response in dev to distinguish empty result from transport error."],"tags":["wallet","encryption","keyring","rpc","mnemonic"],"backgroundTag":"encryption-service-unavailable","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}