Skip to content

Commit b5c8884

Browse files
lklimekclaude
andcommitted
refactor: use concrete SdkError type in error variants; drop dyn Error
- IdentityUpdateTransitionError.source_error: Box<dyn Error> → Box<SdkError> (ProtocolError converts via SdkError::Protocol(e)) - InternalSendError: struct variant → unit variant (SendError carries no diagnostic information worth preserving in the chain) - CLAUDE.md rule 8: document source field type conventions — Box<SdkError> for SDK-originated errors, concrete domain type for non-SDK errors, omit #[source] when upstream carries no diagnostic value Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b5c0f8c commit b5c8884

4 files changed

Lines changed: 6 additions & 10 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ User-facing error messages (shown in `MessageBanner` via `Display`) must follow
7373
5. **i18n-ready**: Write messages as simple, complete sentences without interpolation tricks. Avoid concatenating fragments, positional assumptions, or grammar that breaks in other languages. Messages should be straightforward to extract into [Fluent](https://projectfluent.org/) `.ftl` files later — one message ID per string, placeholders only for dynamic values (`{ $seconds }`, `{ $name }`), no logic in the text itself.
7474
6. **Reference implementation**: `sdk_error_user_message()` in `src/backend_task/error.rs` demonstrates the pattern for SDK errors. New `TaskError` variants should follow the same style.
7575
7. **Base58 IDs are allowed in messages**: Contract IDs, identity IDs, document IDs, and similar Base58-encoded identifiers may appear in user-facing messages when they help the user identify which object is involved (e.g., *"This key conflicts with an existing key bound to contract `Abc123…`."*). They are not jargon — they are opaque-but-copyable handles the user can look up.
76-
8. **Prefer granular `TaskError` variants over `Generic`**: When mapping errors to add context, add a dedicated `TaskError` variant with a `#[source]` field rather than converting to `TaskError::Generic(format!(...))`. Granular variants preserve the error chain, enable structural matching by callers, and make `Display` / `Debug` separation explicit. `TaskError::Generic` is a last resort for one-off strings with no upstream error to preserve.
76+
8. **Prefer granular `TaskError` variants over `Generic`**: When mapping errors to add context, add a dedicated `TaskError` variant with a `#[source]` field rather than converting to `TaskError::Generic(format!(...))`. Granular variants preserve the error chain, enable structural matching by callers, and make `Display` / `Debug` separation explicit. `TaskError::Generic` is a last resort for one-off strings with no upstream error to preserve. For `#[source]` fields in SDK-originated error variants, use `Box<SdkError>` — convert upstream types (e.g. `ProtocolError`) via `SdkError::Protocol(e)`. Use the concrete domain type directly for non-SDK errors (e.g. `rusqlite::Error`). Omit `#[source]` entirely when the upstream error carries no useful diagnostic information (e.g. a channel `SendError`).
7777

7878
## Architecture Overview
7979

src/backend_task/error.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,12 @@ pub enum TaskError {
110110
#[error("Could not build the key update transaction. Please retry.")]
111111
IdentityUpdateTransitionError {
112112
#[source]
113-
source: Box<dyn std::error::Error + Send + Sync>,
113+
source_error: Box<SdkError>,
114114
},
115115

116116
/// Failed to send a result back to the UI — the receiver was dropped.
117117
#[error("Internal update failed. Please retry the operation.")]
118-
InternalSendError {
119-
#[source]
120-
source: Box<dyn std::error::Error + Send + Sync>,
121-
},
118+
InternalSendError,
122119

123120
/// Unclassified SDK error — the operation failed for an unrecognised reason.
124121
/// Display is implemented manually via [`sdk_error_user_message`] to inspect

src/backend_task/identity/add_key_to_identity.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::model::fee_estimation::PlatformFeeEstimator;
66
use crate::model::qualified_identity::PrivateKeyTarget::PrivateKeyOnMainIdentity;
77
use crate::model::qualified_identity::QualifiedIdentity;
88
use crate::model::qualified_identity::qualified_identity_public_key::QualifiedIdentityPublicKey;
9+
use dash_sdk::Error as SdkError;
910
use dash_sdk::Sdk;
1011
use dash_sdk::dpp::identity::accessors::{IdentityGettersV0, IdentitySettersV0};
1112
use dash_sdk::dpp::identity::identity_public_key::accessors::v0::{
@@ -64,7 +65,7 @@ impl AppContext {
6465
None,
6566
)
6667
.map_err(|e| TaskError::IdentityUpdateTransitionError {
67-
source: Box::new(e),
68+
source_error: Box::new(SdkError::Protocol(e)),
6869
})?;
6970

7071
let result = state_transition.broadcast_and_wait(sdk, None).await?;

src/backend_task/identity/refresh_identity.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ impl AppContext {
5555
sender
5656
.send(TaskResult::Refresh)
5757
.await
58-
.map_err(|e| TaskError::InternalSendError {
59-
source: Box::new(e),
60-
})?;
58+
.map_err(|_| TaskError::InternalSendError)?;
6159

6260
Ok(BackendTaskSuccessResult::RefreshedIdentity(
6361
qualified_identity,

0 commit comments

Comments
 (0)