Skip to content
Open
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
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Next

* Improve docstrings of `isRetryPossible` and `isCleanReject` (#402).
* Add `isCleanReject` to `Error`, align reject code order with IC interface specification and improve comments (#401).
* internal: updates `matchers` dev-dependency (#394).
* Add `PriorityQueue` (#392).
Expand Down
26 changes: 21 additions & 5 deletions src/Error.mo
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,20 @@ module {

/// Checks if the error is a clean reject.
/// A clean reject means that there must be no state changes on the callee side.
/// Note that a return value of `false` does not imply the state has been mutated.
/// See `isRetryPossible` for example usage.
public func isCleanReject(error : Error) : Bool = switch (code error) {
case (#system_fatal or #system_transient or #destination_invalid or #call_error _) true;
case _ false
};

/// Returns whether retrying to send a message may result in success.
/// Determines if the failed call can be retried immediately within the update method
/// that's handling the error, as opposed to relying on a background timer or heartbeat.
///
/// A return value of `true` indicates that an immediate retry *might* succeed, i.e., not result in another error.
/// However, the caller is responsible for ensuring that retries are safe in their specific context.
/// For idempotent methods, immediate retries are generally safe. For non-idempotent ones,
/// checking `isCleanReject` before retrying is recommended.
///
/// Example:
/// ```motoko
Expand All @@ -88,15 +96,23 @@ module {
/// public func example(callableActor : CallableActor) {
/// try {
/// await (with timeout = 3) callableActor.call();
/// }
/// catch e {
/// } catch e {
/// Debug.print(Error.message e);
///
/// // Check if an immediate retry might succeed.
/// if (Error.isRetryPossible e) {
/// Debug.print(Error.message e);
/// Debug.print("Immediate retry may succeed. Consider retrying now.");
/// };
///
/// // Check if the failed call is clean (guaranteed not to have mutated state).
/// if (Error.isCleanReject e) {
/// Debug.print("Previous call did not mutate state: safe to retry.");
/// } else {
/// Debug.print("Previous call may have mutated state: retry with caution.");
/// }
/// }
/// }
/// }
///
/// ```
public func isRetryPossible(error : Error) : Bool = switch (code error) {
case (#system_transient or #system_unknown) true;
Expand Down
Loading