I know we’ve discussed this before, but I wanted to rais it again now that I’m using some different motoko structures.
Why can’t I catch a trap: https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/
actor Echo {
// Say the given phase.
public query func say() : async Text {
type test = {
#nat: Nat;
#text : Text;
};
let the_value1 = #nat(6);
let the_value2 = #text("7");
let #text(phrase_1) = try{
the_value1;
} catch(e) {
#text("not a text");
};
let #text(phrase_2) = try{
the_value2;
} catch(e) {
#text("not a text");
};
return phrase_1 # "|" # phrase_2;
};
};
Call failed:
Canister: wxani-naaaa-aaaab-qadgq-cai
Method: say (query)
"Status": "rejected"
"Code": "CanisterError"
"Message": "IC0503: Canister wxani-naaaa-aaaab-qadgq-cai trapped explicitly: pattern failed"
I know try and catch is supposed to be for async calls, but the above is an example that would be nice to do. Why can’t we try/catch regular functions? Is there something inherent to motoko that just can’t recover from these errors? Or is it a design choice? Is it a limitation of wasm?
2 Likes
Traps can indeed not be handled in Wasm. That’s a very intentional limitation of Wasm. Traps are not exceptions, they indicate program states that should never be reached, i.e. plain bugs in the program. When a program reaches such an inconsistent state, an attempt to recover from within the same program would then be dangerous, because it cannot guarantee consistent behaviour anymore – all bets are off. The only safe cause of action is to abort immediately and recover at a higher level.
Unfortunately, Wasm also does not have a real exception mechanism (yet). So for Motoko, there isn’t much choice to use anything else.
That all said, in your program, nothing would ever get caught anyway – the try-catch handlers do not enclose the faulty operation, which is the let-declaration with a non-exhaustive pattern match. You would have to write it as something like this:
let phrase_1 = try {
let #text(phrase_1) = the_value;
phrase_1
} catch (e) {
"not a text"
}
Either way, this code is no better than simply writing a switch. Using try-catch to locally recover from a non-exhaustive pattern match is always gonna be more convoluted than using an exhaustive switch right away. So even if it worked, try-catch wouldn’t make sense for this use case.
7 Likes
Amazing explanation, thanks!
Just to revive this thread with a desperate question:
What are my options when I am using a library that does not handle errors well and just traps in situations where there should be a Result.Result<> returned, so i can handle it. Fork and rewrite the library? lol
The new try, catch, finally may work? I think I saw it will catch regular errors now? But I think it traps regardless after the finally…maybe you can return during the finally if something you expect to happen doesn’t happen?
Maybe set a bDidFinish at the end of the try?
I wrote the library didn’t I.
1 Like
It doesn’t catch internal traps but the finally still runs even if there was an internal trap, so you can clear some state etc in the finally if you need.
But you can’t convert the trap back to a return of some kind. That would be nice. @ggreif
Ya, no luck
I don’t like to encourage bad error handling behavior, but not sure what to do in situations like this. I guess I’ll just try to see if i can modify the library