fix(ui): clear startup transient error banners once network/migration is ready#916
Conversation
Transfer startup-only error banner handles to the connection and migration reconcilers so readiness transitions clear them reactively. <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub> Co-Authored-By: Codex GPT-5 <noreply@openai.com>
📝 WalkthroughWalkthroughThe change classifies masternode-list startup failures, routes storage and quorum readiness errors to dedicated banner trackers, and clears those banners when migration or masternode readiness is restored. ChangesStartup readiness handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant SpvProvider
participant TaskError
participant AppStateUI
participant ConnectionBanner
participant MigrationReconciler
SpvProvider->>TaskError: emit masternode-list-not-ready diagnostic
TaskError->>AppStateUI: classify as MasternodeListNotReady
AppStateUI->>ConnectionBanner: track quorum startup banner
AppStateUI->>MigrationReconciler: track storage startup banner
ConnectionBanner->>ConnectionBanner: clear when masternodes become ready
MigrationReconciler->>MigrationReconciler: clear when storage migration resolves
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
🕓 Ready for review — 39 ahead in queue (commit 841b70d) |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/reconcilers.rs`:
- Around line 517-525: The storage_guard_resolved check in the migration state
handling must exclude transient backend-not-ready failures from being considered
resolved. Update the logic around MigrationState::Failed and
error.is_backend_not_ready() so the storage_startup_error banner is retained
while automatic retry resets the migration state to Idle; continue clearing it
for genuinely resolved states and non-transient failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a87a1a98-7a1b-48d4-a361-5c27cdf963c0
📒 Files selected for processing (4)
src/app.rssrc/app/reconcilers.rssrc/backend_task/error.rssrc/context_provider.rs
| let storage_guard_resolved = !matches!( | ||
| &state, | ||
| MigrationState::Idle | ||
| | MigrationState::Running { .. } | ||
| | MigrationState::AwaitingWalletPasswords { .. } | ||
| ); | ||
| if storage_guard_resolved && let Some(handle) = self.storage_startup_error.take() { | ||
| handle.clear(); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Prevent premature banner clearance during transient migration failures.
If the migration fails transiently due to a slow-wiring backend (error.is_backend_not_ready()), the frame state evaluates to MigrationState::Failed. This satisfies storage_guard_resolved, prematurely clearing the user's active startup error banner. Immediately after, the failure match-arm below resets the migration status back to Idle for a seamless background retry, but the banner is already lost.
To ensure the "storage not ready" warning remains visible to the user while the automatic retry happens, storage_guard_resolved should not treat transient failures as resolved.
🐛 Proposed fix
- let storage_guard_resolved = !matches!(
- &state,
- MigrationState::Idle
- | MigrationState::Running { .. }
- | MigrationState::AwaitingWalletPasswords { .. }
- );
+ let is_transient_failure = match &state {
+ MigrationState::Failed { error }
+ | MigrationState::FailedWithUnreadableIdentities { error, .. } => {
+ error.is_backend_not_ready()
+ }
+ _ => false,
+ };
+ let storage_guard_resolved = !is_transient_failure
+ && !matches!(
+ &state,
+ MigrationState::Idle
+ | MigrationState::Running { .. }
+ | MigrationState::AwaitingWalletPasswords { .. }
+ );
if storage_guard_resolved && let Some(handle) = self.storage_startup_error.take() {
handle.clear();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let storage_guard_resolved = !matches!( | |
| &state, | |
| MigrationState::Idle | |
| | MigrationState::Running { .. } | |
| | MigrationState::AwaitingWalletPasswords { .. } | |
| ); | |
| if storage_guard_resolved && let Some(handle) = self.storage_startup_error.take() { | |
| handle.clear(); | |
| } | |
| let is_transient_failure = match &state { | |
| MigrationState::Failed { error } | |
| | MigrationState::FailedWithUnreadableIdentities { error, .. } => { | |
| error.is_backend_not_ready() | |
| } | |
| _ => false, | |
| }; | |
| let storage_guard_resolved = !is_transient_failure | |
| && !matches!( | |
| &state, | |
| MigrationState::Idle | |
| | MigrationState::Running { .. } | |
| | MigrationState::AwaitingWalletPasswords { .. } | |
| ); | |
| if storage_guard_resolved && let Some(handle) = self.storage_startup_error.take() { | |
| handle.clear(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/reconcilers.rs` around lines 517 - 525, The storage_guard_resolved
check in the migration state handling must exclude transient backend-not-ready
failures from being considered resolved. Update the logic around
MigrationState::Failed and error.is_backend_not_ready() so the
storage_startup_error banner is retained while automatic retry resets the
migration state to Idle; continue clearing it for genuinely resolved states and
non-transient failures.
… reconciler banner tracking The reactive storage-error banner cleared as soon as MigrationState went terminal, but the detached best-effort DAPI-node refresh can still hold migration_run for a real busy window after that point — a retry in that window failed silently with the banner already gone. Gate the clear on both the terminal state AND a non-blocking migration_run.try_lock() probe. Also extracts the repeated adopt/reset/conditional-clear banner-handle pattern shared by ConnectionBanner and MigrationReconciler into a small TransientBanner newtype, and hardens the SDK error-string sentinel test to pin the literal upstream string shape instead of round-tripping the same constant through itself. Addresses grumpy-review findings CODE-001, CODE-003, PROJ-001. Co-Authored-By: Codex Sol <noreply@openai.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Brings in: shutdown fix (dashpay#905), duplicate-DPNS-name error message (dashpay#915), startup banner clearing (dashpay#916), nav pointer cursor + tooltips and wallet-less masternode indication (dashpay#917), onboarding disconnected- banner suppression (dashpay#907), masternode dialog/nav/passphrase fixes (dashpay#913), DAPI auto-refresh during pre-1.0 migration (dashpay#908), "Add Receiving Address" wiring + its test hardening (dashpay#914, dashpay#920), and a CI timeout bump (dashpay#912). dashpay#906 (shielded re-enable) was already pulled in individually last session, so its squashed commit merged as a no-op. Conflicts (6 files) were rebrand-naming overlaps (dash_evo_tool:: vs orchardpay:: imports) plus one real merge in left_panel.rs, where OrchardPay's green-icon tint had to combine with upstream's new nav tooltip. Also fixed 5 files upstream's auto-merged (non-conflicting) additions left un-rebranded: a stray DASH_EVO_DATA_DIR_LOCK/env-var name in a new app.rs test, and dash_evo_tool:: references in three kittest test files. Added tooltip strings for OrchardPay's own nav entries (OrchardPay, DashPay) so the new every_nav_entry_has_a_tooltip test covers them — upstream's version only knows its own nav items. Fixed the new nav_label_hover_shows_pointer_cursor kittest test: OrchardPay's nav rail carries two more always-visible entries than upstream's, pushing "Settings" below the scrollable list's default-size visible viewport; scroll it into view first, matching what a real user would do. Verified: cargo check (both feature modes), cargo clippy --all-features --all-targets -- -D warnings, cargo fmt --all, cargo test --all-features --workspace (2066 lib + 257 kittest + doc tests, 0 failed), all clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TL;DR: Keep the "storage still updating" banner visible until storage is genuinely free again, instead of letting it disappear about a frame too early.
User story
As a user who just finished the app's first-run migration, I want the "storage update is still running" message to stay up until storage is truly available, so a retried action that fails during that window isn't left unexplained.
Scenario
Base flow
The app finishes cold-start migration; the user immediately retries a wallet-touching action.
Actual behavior
Two error banners could persist indefinitely during app cold start ("storage update is still running" and a masternode-list-not-yet-synced error) — even minutes after the underlying condition fully resolved. The initial fix for that made the storage banner reactive, but it could still clear about one frame before storage was actually free: a background best-effort node-list refresh kept holding the lock past the point the migration state reported "ready." A retry in that narrow window failed again with the banner already gone, looking like an unexplained glitch.
Expected behavior
Both banners clear reactively once their condition is genuinely resolved, and the storage banner specifically stays up until storage is truly free — so a failed retry in that window has a visible, honest explanation instead of none.
Detailed discussion
What was done
TaskError::MasternodeListNotReadyvariant with a clean, actionable message instead of falling through to the generic, opaqueSdkErrortext.ConnectionBanner/MigrationReconciler(app/reconcilers.rs) now adopt these banner handles and clear them reactively once their readiness condition genuinely holds, instead of leaving themdisable_auto_dismiss()'d forever.storage_guard_resolvedwas computed fromMigrationStatealone, so it could flip true (and clear the banner) before themigration_runmutex the error actually reports on was released — a detached best-effort DAPI-node refresh keeps holding it pastMigrationStategoingReady. Gated the clear on both the terminal state AND a non-blockingmigration_run.try_lock()probe.ConnectionBannerandMigrationReconciler) into a smallTransientBannernewtype — same behavior, less duplication.context_provider.rsandbackend_task/error.rsis discoverable.claudius:grumpy-reviewfindings CODE-001 (blocking), CODE-003 and PROJ-001 (non-blocking). CODE-002 (a pre-existing FIFO-eviction edge case in the "stuck offline forever" scenario) is a known, accepted follow-up — not fixed here.Testing
--libscope.cargo clippy --bin dash-evo-tool --all-features -- -D warningsclean,cargo fmt --allclean.Breaking changes
None.
Checklist
Prior work
Attribution
🤖 Co-authored by Claudius the Magnificent AI Agent