Skip to content

fix(dashpay): fix DPNS username resolution in contact requests#723

Closed
thepastaclaw wants to merge 4 commits into
dashpay:v1.0-devfrom
thepastaclaw:fix/add-contact-username-resolution
Closed

fix(dashpay): fix DPNS username resolution in contact requests#723
thepastaclaw wants to merge 4 commits into
dashpay:v1.0-devfrom
thepastaclaw:fix/add-contact-username-resolution

Conversation

@thepastaclaw
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw commented Mar 10, 2026

Issue

Fixes #687

Description

Adding a contact by username silently fails — the DPNS lookup always returns empty results.

Root Cause

resolve_username_to_identity was missing the required normalizedParentDomainName where clause in the DPNS document query. DPNS uses a compound index on (normalizedParentDomainName, normalizedLabel) — querying with only normalizedLabel returns empty results silently.

Fix

  1. Added normalizedParentDomainName = "dash" where clause — this is the primary fix that makes the DPNS query actually match the document type index
  2. Changed identity resolution from document.owner_id() to records.identity — the DPNS document's records.identity field is the authoritative identity reference, which may differ from the document owner
  3. Added convert_to_homograph_safe_chars normalization — consistent with how DPNS registration normalizes labels
  4. Uses cached dpns_contract from app_context instead of fetching it from the network on every lookup

Testing

  • cargo check
  • cargo clippy

Summary by CodeRabbit

  • Refactor

    • Improved username-to-identity resolution with safer normalization, broader resolution paths, and more robust identity extraction.
  • Bug Fixes / Reliability

    • Added retry logic for transient lookup and identity fetch failures to reduce intermittent errors.
    • Standardized user-facing network error for temporary platform sync issues to a clear "temporarily out of sync" message.

The resolve_username_to_identity function had three issues that caused
'Add Contact by Username' to fail silently:

1. Missing normalizedParentDomainName where clause - DPNS queries
   require both normalizedParentDomainName and normalizedLabel for
   the domain document type index. Without it, the query returns
   empty results even for existing usernames.

2. Used document.owner_id() instead of records.identity - The DPNS
   domain document's owner may differ from the identity the name
   resolves to. The correct field is records.identity.

3. No homograph-safe normalization - The label was only lowercased
   but not run through convert_to_homograph_safe_chars, which is
   required for consistent DPNS lookups.

Additionally, replaced the redundant DPNS contract fetch with the
app_context's cached dpns_contract, consistent with the rest of the
codebase.

Closes dashpay#687
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38f8d014-55ae-4032-bca7-8bd30cfd57d6

📥 Commits

Reviewing files that changed from the base of the PR and between e7bb91f and 1873064.

📒 Files selected for processing (2)
  • src/backend_task/dashpay/contact_requests.rs
  • src/backend_task/dashpay/errors.rs

📝 Walkthrough

Walkthrough

resolve_username_to_identity now accepts AppContext, uses app_context.dpns_contract to query DPNS by homograph-safe normalized label and normalizedParentDomainName == "dash", extracts identity from records.identity, adds retry logic for DPNS and identity fetches, and maps certain server-state messages to a NetworkError UI path.

Changes

Cohort / File(s) Summary
DPNS Username Resolution
src/backend_task/dashpay/contact_requests.rs
Changed signature to resolve_username_to_identity(app_context: &Arc<AppContext>, sdk: &Sdk, username: &str); normalize username/label (homograph-safe), query DPNS via app_context.dpns_contract filtering normalizedParentDomainName == "dash" and normalizedLabel; extract identity from records.identity; add retry logic for transient platform-sync errors when querying DPNS and fetching identities; update downstream call sites to new signature and error semantics.
UI Error Mapping
src/ui/dashpay/add_contact_screen.rs
Added pre-check mapping backend messages containing "try another server", "height is outdated", or "temporarily out of sync" to DashPayError::NetworkError with a standardized platform-sync reason, evaluated before generic network/connection handling.
Error Messaging
src/backend_task/dashpay/errors.rs
DashPayError::user_message updated to return a standardized "Platform servers are temporarily out of sync. Please try again in a moment." when the embedded network reason contains the platform-sync indicators; otherwise preserves the generic network message.

Sequence Diagram(s)

sequenceDiagram
    participant UI as "Add Contact UI"
    participant Task as "Backend Task"
    participant App as "AppContext (dpns_contract)"
    participant DPNS as "DPNS Service"
    participant IDSvc as "Identity Service"

    UI->>Task: send_contact_request_with_proof(username)
    Task->>App: resolve_username_to_identity(app_context, sdk, username)
    App->>DPNS: queryDocuments(filter: normalizedParentDomainName=="dash", normalizedLabel)
    alt transient DPNS error
        DPNS-->>App: error (e.g., "height is outdated"/"try another server")
        App->>DPNS: queryDocuments (retry up to MAX_RETRIES)
    end
    DPNS-->>App: DPNS document (records.identity)
    App->>IDSvc: fetchIdentity(records.identity)
    alt transient identity fetch error
        IDSvc-->>App: error (transient)
        App->>IDSvc: fetchIdentity (retry up to MAX_RETRIES)
    end
    IDSvc-->>App: Identity
    App-->>Task: Identity
    Task-->>UI: success / mapped NetworkError (platform-sync)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I nibbled labels, cleaned each name,
Retries kept me hopping through error and blame,
DPNS trails led me to identity true,
Server-sync hiccups I softly withdrew,
Now contacts bloom where usernames grew 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(dashpay): fix DPNS username resolution in contact requests' directly matches the main change—fixing DPNS username resolution for contact request flows.
Linked Issues check ✅ Passed The PR implements all coding objectives from #687: DPNS query normalization, identity extraction from records.identity, homograph-safe normalization, cached contract usage, and retry logic for transient errors with UI mapping.
Out of Scope Changes check ✅ Passed All changes are within scope: DPNS resolution fixes, retry logic, identity extraction, and error-handling updates directly address #687. No unrelated refactoring or scope creep detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Added retry logic for transient "height is outdated" / "try another server" errors (up to 3 retries on both DPNS query and identity fetch). Also added UI-side mapping so these errors show a user-friendly message and the Retry button.

This subsumes the fix from tracker-529 (same issue, different manifestation).

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 11, 2026

@thepastaclaw Sure! I'll review the latest changes including the retry logic for transient DPNS/identity-fetch errors and the UI-side error mapping.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/backend_task/dashpay/contact_requests.rs (1)

185-205: ⚠️ Potential issue | 🟡 Minor

The retry fix only covers usernames right now.

The .dash paths now benefit from the new retry logic, but the direct identity-ID branch still does a one-shot Identity::fetch. Transient "height is outdated" / "try another server" failures will still surface immediately for the other supported input mode, so this reliability fix is only applied to half of the flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend_task/dashpay/contact_requests.rs` around lines 185 - 205, The
identity-ID branch currently does a one-shot Identity::fetch(sdk, to_id) whereas
the username paths use the new retry logic; update the to_identity resolution so
Identity::fetch is retried on transient RPC errors (e.g., messages containing
"height is outdated" or "try another server") similar to
resolve_username_to_identity's retry/backoff behavior. Locate the match arm that
handles Identifier::from_string_try_encodings and replace the direct
Identity::fetch call with the same retry wrapper or loop used for usernames (or
extract the retry logic into a helper and call it here), preserving the existing
error mapping (map_err/map_ok and not-found handling) so failures after retries
still produce the same formatted errors.
🧹 Nitpick comments (1)
src/backend_task/dashpay/contact_requests.rs (1)

507-511: Avoid extending the stringly typed error contract here.

These new branches add more backend error text that AddContactScreen has to classify by substring. Please surface a typed TaskError with a DashPay-specific variant instead of more String cases, otherwise small wording changes will keep breaking the UI mapping.

As per coding guidelines, "Backend tasks should return Result<T, TaskError> from src/backend_task/error.rs. When adding new backend error types, add a #[from] variant to TaskError rather than converting to String."

Also applies to: 559-563, 621-627

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/backend_task/dashpay/contact_requests.rs` around lines 507 - 511, Change
the function signature of resolve_username_to_identity to return
Result<Identity, TaskError> (import TaskError from src/backend_task/error.rs),
stop returning raw String errors and instead return a typed DashPay-specific
TaskError variant; add a new DashPayError type (or similar) and add a #[from]
variant to TaskError like DashPay(#[from] DashPayError) so you can convert
concrete DashPay errors into TaskError easily, replace all
Err(String::from(...)) in resolve_username_to_identity (and the other similar
branches noted) with Err(DashPayError::new(...).into()) or
Err(DashPayErrorVariant.into()), and update callers to handle TaskError
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/dashpay/add_contact_screen.rs`:
- Around line 656-662: The branch in AddContactScreen constructs
DashPayError::NetworkError with a specific reason string but
DashPayError::user_message() currently ignores NetworkError.reason and returns a
generic message; update the error handling so the UI can show the curated reason
by either (a) modifying DashPayError::user_message() to return
NetworkError.reason when present (preserve generic fallback), or (b) introducing
a new enum variant (e.g., DashPayError::PlatformOutOfSync or
DashPayError::RecoverableError) used by AddContactScreen and handled in
user_message() to return the specific out-of-sync text; change the code paths
that construct NetworkError in AddContactScreen to use the new variant if you
choose option (b).

---

Outside diff comments:
In `@src/backend_task/dashpay/contact_requests.rs`:
- Around line 185-205: The identity-ID branch currently does a one-shot
Identity::fetch(sdk, to_id) whereas the username paths use the new retry logic;
update the to_identity resolution so Identity::fetch is retried on transient RPC
errors (e.g., messages containing "height is outdated" or "try another server")
similar to resolve_username_to_identity's retry/backoff behavior. Locate the
match arm that handles Identifier::from_string_try_encodings and replace the
direct Identity::fetch call with the same retry wrapper or loop used for
usernames (or extract the retry logic into a helper and call it here),
preserving the existing error mapping (map_err/map_ok and not-found handling) so
failures after retries still produce the same formatted errors.

---

Nitpick comments:
In `@src/backend_task/dashpay/contact_requests.rs`:
- Around line 507-511: Change the function signature of
resolve_username_to_identity to return Result<Identity, TaskError> (import
TaskError from src/backend_task/error.rs), stop returning raw String errors and
instead return a typed DashPay-specific TaskError variant; add a new
DashPayError type (or similar) and add a #[from] variant to TaskError like
DashPay(#[from] DashPayError) so you can convert concrete DashPay errors into
TaskError easily, replace all Err(String::from(...)) in
resolve_username_to_identity (and the other similar branches noted) with
Err(DashPayError::new(...).into()) or Err(DashPayErrorVariant.into()), and
update callers to handle TaskError accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fad7b015-1281-43b9-a9ff-8ca1ad2d391c

📥 Commits

Reviewing files that changed from the base of the PR and between 788882d and beef9f7.

📒 Files selected for processing (2)
  • src/backend_task/dashpay/contact_requests.rs
  • src/ui/dashpay/add_contact_screen.rs

Comment thread src/ui/dashpay/add_contact_screen.rs
- Add retry logic for transient platform errors in the identity-ID
  branch of send_contact_request_with_proof, matching the existing
  retry behavior in resolve_username_to_identity
- Update DashPayError::user_message() to surface curated platform
  out-of-sync reasons from NetworkError instead of always showing
  generic network text
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

The two actionable findings from CodeRabbit's review (retry logic missing on the identity-ID branch, and NetworkError.reason not surfaced in user_message()) were addressed in e7bb91f.

The remaining nitpick about switching from String errors to typed TaskError variants is valid architecturally, but it's a larger refactor that touches the entire DashPay backend error flow. Deferring to a follow-up issue to keep this PR focused on the DPNS resolution fix.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 11, 2026

@thepastaclaw Thanks for the update! Acknowledged on both points:

  • The two actionable findings (retry on the identity-ID branch + NetworkError.reason in user_message()) are confirmed addressed in e7bb91f.
  • The String → typed TaskError refactor deferral makes sense — keeping this PR scoped to the DPNS resolution fix is the right call.

Would you like me to open a follow-up GitHub issue to track the typed error refactor for the DashPay backend error flow?

I'll kick off the incremental review of the latest changes now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/backend_task/dashpay/contact_requests.rs`:
- Around line 207-224: The retry branches that currently check err.contains("try
another server") and err.contains("height is outdated") should also check for
the SDK/server wording "temporarily out of sync" so transient errors are treated
consistently with DashPayError::user_message(); update the conditional in the
identity-fetch retry block (references: retries, MAX_RETRIES, to_username_or_id,
e) to include err.contains("temporarily out of sync") and mirror the same change
in the other two retry blocks noted (the blocks around the other occurrences at
the regions you flagged), ensuring the same retry/increment/continue behavior
and the same final Err(...) message when exhausted.
- Around line 542-546: Replace the current naive split that truncates on the
first '.' by explicitly accepting only an optional ".dash" suffix: check if the
input string `username` ends_with(".dash") and if so set `name` =
username.strip_suffix(".dash").unwrap(); otherwise, if `username` contains any
'.' return an error; finally ensure `name` is non-empty and contains no '.'
before proceeding (update the error message used in the existing ok_or_else to
reflect invalid dotted usernames). This uses the existing `username` and `name`
identifiers so you can locate the logic and safely reject malformed dotted
inputs instead of truncating them.

In `@src/backend_task/dashpay/errors.rs`:
- Around line 145-155: The DashPayError::NetworkError arm currently returns the
raw backend `reason` when it contains platform-sync phrases; change it to return
a single curated user-facing string instead of `reason.clone()` so the Retry
copy doesn't leak SDK/transport details. In the match arm that checks
`reason.contains("temporarily out of sync") || reason.contains("height is
outdated") || reason.contains("try another server")`, replace returning
`reason.clone()` with a fixed message like "Wallet data temporarily out of sync,
please try again shortly." and keep the existing generic fallback for all other
cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c9ecccfe-580c-42be-91c1-97bddda62060

📥 Commits

Reviewing files that changed from the base of the PR and between beef9f7 and e7bb91f.

📒 Files selected for processing (2)
  • src/backend_task/dashpay/contact_requests.rs
  • src/backend_task/dashpay/errors.rs

Comment thread src/backend_task/dashpay/contact_requests.rs Outdated
Comment thread src/backend_task/dashpay/contact_requests.rs Outdated
Comment thread src/backend_task/dashpay/errors.rs
- Extract is_transient_platform_sync_error() helper for consistent
  transient error detection across all retry paths, including
  'temporarily out of sync' alongside existing checks
- Reject dotted usernames instead of silently truncating them:
  only accept 'alice' or 'alice.dash', reject 'alice.test' etc.
- Return fixed user-facing message in NetworkError::user_message()
  instead of leaking raw SDK/transport reason strings
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Addressed all 3 findings from the second CodeRabbit review:

  1. Consistent transient error detection — Added "temporarily out of sync" to all 3 retry blocks so it triggers retries consistently with DashPayError::user_message().

  2. Reject dotted usernamessplit('.').next() silently truncated alice.testalice. Now explicitly accepts only optional .dash suffix and rejects other dotted input.

  3. Curated error messageDashPayError::user_message() returns a fixed user-facing string instead of leaking raw SDK/transport text.

@coderabbitai review

@thepastaclaw thepastaclaw force-pushed the fix/add-contact-username-resolution branch from fdc4bca to 1873064 Compare March 11, 2026 07:44
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Third round of CodeRabbit feedback addressed:

  1. Extracted is_transient_platform_sync_error() helper — consolidates all transient error string checks into one function, used across all retry blocks
  2. Fixed username parsing — rejects dotted usernames like alice.test instead of silently truncating to alice; only accepts bare names or .dash suffix
  3. Fixed NetworkError::user_message() — returns a fixed user-facing string instead of leaking raw SDK reason text

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 11, 2026

@thepastaclaw Thanks for the thorough follow-through! All three findings look well-addressed:

  1. Consistent transient error detection — Adding "temporarily out of sync" to all three retry blocks ensures the retry heuristic and the user_message() curated string share the same trigger set. No more gaps where the UI shows a friendly message but the backend doesn't retry.

  2. Reject dotted usernames — The explicit .dash-suffix-only acceptance is the right guard. The old split('.').next() silent truncation was a correctness hazard (e.g., alice.test resolving as alice).

  3. Curated error message — Returning a fixed user-facing string instead of raw SDK/transport text is the correct UX pattern here, especially for transient server-state errors.

Kicking off the incremental review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 11, 2026

@thepastaclaw Thanks for the continued iteration! All three changes look like solid improvements:

  1. is_transient_platform_sync_error() helper — Centralizing the transient error string checks into a single function eliminates the risk of the retry condition and the user_message() condition drifting out of sync again in future edits. Good defensive pattern.

  2. Username parsing fix — Explicitly gating on the .dash suffix and rejecting other dotted forms is a much safer contract than the silent split('.').next() truncation. This prevents subtle correctness bugs (e.g., alice.testalice) that could be very hard to debug from user reports.

  3. NetworkError::user_message() curated string — Returning a fixed, controlled string is the right UX boundary. Raw SDK/transport text leaking into the UI is a common source of confusing error messages for end users.

Kicking off the incremental review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Collaborator Author

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

DPNS username resolution fix is correct — the core issue (missing normalizedParentDomainName in compound-index query) is properly addressed. Also fixes identity extraction, adds homograph normalization, and adds retry logic. Two maintainability suggestions: retry logic is copy-pasted 3 times (extract a helper), and the invalid-username error message still references the old .dash-only rule.

Reviewed commit: 1873064

🟡 2 suggestion(s)

1 additional finding

🟡 suggestion: Error message still describes old .dash-only rule

src/backend_task/dashpay/errors.rs (line 172)

resolve_username_to_identity() now accepts both bare labels and fully qualified names, but InvalidUsername::user_message() still tells users usernames must end with .dash. The error text should be updated to match the new parsing rule: accept 'alice' or 'alice.dash', reject other dotted forms.

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/backend_task/dashpay/contact_requests.rs`:
- [SUGGESTION] lines 207-232: Retry logic copy-pasted 3 times with no backoff
  The same retry loop pattern (loop + is_transient_platform_sync_error check + increment + continue) is duplicated at lines 207-232, 575-596, and 633-659. Extract a generic async retry helper to reduce maintenance drift risk.

In `src/backend_task/dashpay/errors.rs`:
- [SUGGESTION] line 172: Error message still describes old .dash-only rule
  resolve_username_to_identity() now accepts both bare labels and fully qualified names, but InvalidUsername::user_message() still tells users usernames must end with .dash. The error text should be updated to match the new parsing rule: accept 'alice' or 'alice.dash', reject other dotted forms.

Comment on lines +207 to +232
loop {
match Identity::fetch(sdk, to_id).await {
Ok(Some(identity)) => break identity,
Ok(None) => {
return Err(format!("Identity {} not found", to_username_or_id));
}
Err(e) => {
let err = e.to_string();
if is_transient_platform_sync_error(&err) && retries < MAX_RETRIES {
retries += 1;
tracing::warn!(
"Retrying identity fetch for '{}' (attempt {}/{}): {}",
to_username_or_id,
retries,
MAX_RETRIES,
e
);
continue;
}
if is_transient_platform_sync_error(&err) {
return Err("Platform servers are temporarily out of sync. Please try again in a moment.".to_string());
}
return Err(format!("Failed to fetch identity: {}", e));
}
}
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Retry logic copy-pasted 3 times with no backoff

The same retry loop pattern (loop + is_transient_platform_sync_error check + increment + continue) is duplicated at lines 207-232, 575-596, and 633-659. Extract a generic async retry helper to reduce maintenance drift risk.

source: ['claude-general']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/backend_task/dashpay/contact_requests.rs`:
- [SUGGESTION] lines 207-232: Retry logic copy-pasted 3 times with no backoff
  The same retry loop pattern (loop + is_transient_platform_sync_error check + increment + continue) is duplicated at lines 207-232, 575-596, and 633-659. Extract a generic async retry helper to reduce maintenance drift risk.

@lklimek
Copy link
Copy Markdown
Contributor

lklimek commented Apr 8, 2026

Superseded by #810

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

impossible to Add Contact by Username

2 participants