Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion e2e/tests-dfx/error_context.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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 <controller principal to add> <canister id/name or --all> \(--network ic\)'" # this is part of the solution
}
}

@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 <PRINCIPAL> --identity <IDENTITY>"
}
37 changes: 37 additions & 0 deletions src/dfx/src/lib/diagnosis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis {
}

if let Some(agent_err) = err.downcast_ref::<AgentError>() {
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() {
Expand Down Expand Up @@ -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.";
Expand Down Expand Up @@ -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 <PRINCIPAL> --identity <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()))
}