{
 "number": 35966,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35966",
 "title": "wallet, rpc: log instead of returning false",
 "author": "vicjuma",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-08-13T22:26:22Z",
 "updated_at": "2026-08-17T23:01:42Z",
 "age_days": 34,
 "draft": false,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "d682e5fddfea4a88e12d47a38769db99d44b65fb",
 "head_ref": "fix-misleading-wallet-encryption-error",
 "head_repo": "vicjuma/bitcoin",
 "head_history": [],
 "additions": 1,
 "deletions": 1,
 "changed_files": 1,
 "commit_count": 1,
 "size_bucket": "S",
 "mergeable_state": "blocked",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": []
  }
 },
 "acks_parsed": {},
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "achow101",
   "katesalazar"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "blocked",
  "last_author_activity": "2026-08-17T23:00:58Z",
  "last_reviewer_activity": "2026-08-17T22:47:37Z",
  "last_reviewer": "achow101",
  "author_silent_days": 30,
  "waiting_on_author_days": 0,
  "days_since_update": 30
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "### Summary\n\n```cpp\n  if (!Unlock(strWalletPassphrase)) {\n            return false;\n    }\n```\n\nThe above code `src/wallet/wallet.cpp:890: ` runs after the wallet encryption process is already complete. Making the failed unlock operation to return false leads to a misleading RPC error message, just in case the encryption happens but the unlock operation fails. Probably it was added there because the function should always return a boolean.\n\n### Before change\n\nThis is the result of a failed unlock, i.e making the function to always return false to mimic the unlock failure\n\n**Mutated function**\n```cpp\nif (Unlock(plain_master_key)) {\n        // Now that we've unlocked, upgrade the descriptor cache\n        // UpgradeDescriptorCache();\n         return false;\n    }\n```\n\n**Output**\n\n```console\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$ ./bitcoin-cli createwallet testwallet\n{\n  \"name\": \"testwallet\"\n}\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$ ./bitcoin-cli encryptwallet \"testpass\"\nerror code: -16\nerror message:\nError: Failed to encrypt the wallet.\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$ ./bitcoin-cli encryptwallet \"testpass\"\nerror code: -15\nerror message:\nError: running with an encrypted wallet, but encryptwallet was called.\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$\n```\n\n### Solution\n\nThe failed unlock is inconsequential to the encryption process. It can even just be written as\n\n```cpp\nLock();\nUnlock(strWalletPassphrase)\n\nSetupWalletGeneration();\n\nLock();\n```\nInstead of returning false, which leads to a misleading error message to the user, probably just logging a message might be necessary\n\n```cpp\n if (!Unlock(strWalletPassphrase)) {\n    WalletLogPrintf(\"Unlocking the encrypted wallet failed\\n\");\n }\n```\n\n**After the change**\n ```console\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$ ./bitcoin-cli createwallet testwallet\n{\n  \"name\": \"testwallet\"\n}\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$ ./bitcoin-cli encryptwallet \"testpass\"\nwallet encrypted; The keypool has been flushed and a new HD seed was generated. You need to make a new backup with the backupwallet RPC.\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$ ./bitcoin-cli encryptwallet \"testpass\"\nerror code: -15\nerror message:\nError: running with an encrypted wallet, but encryptwallet was called.\nratedg@0xratedg:~/Desktop/bitcoin/build/bin$\n```\nThe nature of the log is the one to be probably determined or the essence of the conditional statement",
 "commits": [
  {
   "sha": "d682e5fddfea4a88e12d47a38769db99d44b65fb",
   "date": "2026-08-13T21:43:04Z",
   "message": "wallet, rpc: log instead of returning false\n\nreplace the false return after encryption on !Unlock"
  }
 ],
 "timeline": [
  {
   "t": "2026-08-14T08:14:25Z",
   "kind": "comment",
   "who": "katesalazar",
   "assoc": "CONTRIBUTOR",
   "text": "Is this consistent with PR 28333?\nCan `GetDatabase().Rewrite();` get called in a dangeruz way?"
  },
  {
   "t": "2026-08-14T08:37:52Z",
   "kind": "comment",
   "who": "vicjuma",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nThanks for the comment. Both `GetDatabase().Rewrite();` and `!Unlock(strWalletPassphrase)` are called after the encryption is already successful. This PR does not look at the database rewrite in any way because its result does not also change anything. Whether the rewrite is a success or not, the encryption has already happened. Same as whether the unlock is successful or not. That's why returning false raises the wrong error message as I have demonstrated above."
  },
  {
   "t": "2026-08-14T08:44:24Z",
   "kind": "comment",
   "who": "vicjuma",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nThis PR you are mentioning added the conditional statement, this PR tends to maintein it, only this time logging instead of returning. It is the simplest way if the function is to return a bool. Changing the function to return an enum class variant is also possible, like\n\n```cpp\nenum class EncryptWalletResul {SUCCESS, FAILED, SUCCESS_UNLOCK_FAILED, SUCCESS_REWRITE_FAILED};\n```\n\nThis is however complicated solution for the simple problem"
  },
  {
   "t": "2026-08-14T11:38:08Z",
   "kind": "comment",
   "who": "katesalazar",
   "assoc": "CONTRIBUTOR",
   "text": "I know I'm missing most of the thing here, but. If encryption of right-just-created wallets is currently broken, as I think your bitcoin-cli flow implies, isnt this change a lazy workaround fix instead of what would be a thorough elaborate fix? I can't see how this would be equivalent."
  },
  {
   "t": "2026-08-14T12:05:14Z",
   "kind": "comment",
   "who": "vicjuma",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nIt is not a break in encryption, its just the wrong error reporting. The process is `Encryption -> Unlock Wallet -> Rewrite Database`. Now returning false in the place of unlock is what results in\n\n```console\nerror code: -16\nerror message:\nError: Failed to encrypt the wallet.\n```\nwhich should not be the case because the encryption process has already happened. You can maybe try to reproduce it in regtest so that you see what is currently happening. The PR description shows the steps to reproduce it"
  },
  {
   "t": "2026-08-14T13:55:19Z",
   "kind": "comment",
   "who": "katesalazar",
   "assoc": "CONTRIBUTOR",
   "text": "I'm not familiar with the wallet code, but I don't really feel like returning enum class is much of an overkill for wallet manipulation if you wanna edit this or as an idea for subsequent PRs if achow or whoever oversees wallet these days doesnt veto."
  },
  {
   "t": "2026-08-17T22:47:37Z",
   "kind": "comment",
   "who": "achow101",
   "assoc": "MEMBER",
   "text": "I don't think this is correct. If a wallet is successfully encrypted, and then we are unable to unlock it, that is a catastrophic failure. It means that the wallet is corrupted, and the funds are probably lost. This should really be an `Assert` or some failure mode which results in the wallet no longer being loaded and in use. Logging a warning is not the right thing to do. Returning `false` is reasonable, but not actually far enough in preventing usage of the wallet as we really should be if this were to occur."
  },
  {
   "t": "2026-08-17T22:57:27Z",
   "kind": "comment",
   "who": "vicjuma",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nLogging may not be right, this I did just to show that there might be an intermediate return value (instead of just a true or false), returning an internal RPC error may be appropriate here instead of telling the user that the encryption failed while it actually passed, just that maybe there an issue in the unlock process."
  },
  {
   "t": "2026-08-17T23:00:58Z",
   "kind": "comment",
   "who": "vicjuma",
   "assoc": "CONTRIBUTOR",
   "text": "```cpp\n[quoted text omitted]\n```\nAs can be seen in this demonstration"
  }
 ],
 "labels_log": [],
 "state_log": [],
 "text_chars": 5632,
 "text_tokens_estimate": 1408,
 "changed_paths": [
  "src/wallet/wallet.cpp"
 ],
 "files": [
  {
   "path": "src/wallet/wallet.cpp",
   "add": 1,
   "del": 1
  }
 ],
 "test_lines": 0,
 "git": {
  "head": "d682e5fddfea4a88e12d47a38769db99d44b65fb",
  "head_matches_backup": true,
  "base": "11090c8bb359f894ef7d97b65aff52fe8191aec1",
  "commits": [
   {
    "sha": "d682e5fddf",
    "subject": "wallet, rpc: log instead of returning false",
    "files": 1,
    "add": 1,
    "del": 1
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "abad21c6c925b635",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}