diff --git a/e2e/tests-dfx/error_context.bash b/e2e/tests-dfx/error_context.bash index bc810204e9..dbfa9402a3 100644 --- a/e2e/tests-dfx/error_context.bash +++ b/e2e/tests-dfx/error_context.bash @@ -199,4 +199,15 @@ teardown() { assert_command_fail dfx canister status hello_backend assert_match "not part of the controllers" # this is part of the error explanation assert_match "'dfx canister update-settings --add-controller \(--network ic\)'" # this is part of the solution -} \ No newline at end of file +} + +@test "bad wallet canisters get diagnosed" { + dfx_new hello + dfx_start + dfx deploy hello_backend --no-wallet + id=$(dfx canister id hello_backend) + dfx identity set-wallet "$id" --force + assert_command_fail dfx wallet balance + assert_contains "it did not contain a function that dfx was looking for" + assert_contains "dfx identity set-wallet --identity " +} diff --git a/src/dfx/src/lib/diagnosis.rs b/src/dfx/src/lib/diagnosis.rs index 16975f6640..eb121f14ab 100644 --- a/src/dfx/src/lib/diagnosis.rs +++ b/src/dfx/src/lib/diagnosis.rs @@ -45,6 +45,9 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis { } if let Some(agent_err) = err.downcast_ref::() { + if wallet_method_not_found(agent_err) { + return diagnose_bad_wallet(); + } if not_a_controller(agent_err) { return diagnose_http_403(); } else if *agent_err == AgentError::CertificateNotAuthorized() { @@ -82,6 +85,22 @@ fn not_a_controller(err: &AgentError) -> bool { matches!(std::str::from_utf8(payload.content.as_slice()), Ok("Wrong sender"))) } +fn wallet_method_not_found(err: &AgentError) -> bool { + match err { + AgentError::CertifiedReject(RejectResponse { + reject_code: RejectCode::CanisterError, + reject_message, + .. + }) if reject_message.contains("Canister has no update method 'wallet_") => true, + AgentError::UncertifiedReject(RejectResponse { + reject_code: RejectCode::CanisterError, + reject_message, + .. + }) if reject_message.contains("Canister has no query method 'wallet_") => true, + _ => false, + } +} + fn diagnose_http_403() -> Diagnosis { let error_explanation = "Each canister has a set of controllers. Only those controllers have access to the canister's management functions (like install_code or stop_canister).\n\ The principal you are using to call a management function is not part of the controllers."; @@ -150,3 +169,21 @@ See also release notes: https://forum.dfinity.org/t/dfx-0-11-0-is-promoted-with- (Some(explanation.to_string()), Some(suggestion.to_string())) } + +fn diagnose_bad_wallet() -> Diagnosis { + let explanation = "\ +A wallet has been previously configured (e.g. via `dfx identity set-wallet`). +However, it did not contain a function that dfx was looking for. +This may be because: + - a wallet was correctly installed, but is outdated + - `dfx identity set-wallet` was used on a non-wallet canister"; + let suggestion = "\ +If you have had the wallet for a while, then you may need to update it with +`dfx wallet upgrade`. The release notes indicate when there is a new wallet. +If you recently ran `dfx identity set-wallet`, and the canister may have been +wrong, you can set a new wallet with +`dfx identity set-wallet --identity `. +If you're using a local replica and configuring a wallet was a mistake, you can +recreate the replica with `dfx stop && dfx start --clean` to start over."; + (Some(explanation.to_string()), Some(suggestion.to_string())) +}