fix(node): surface why an embedded daemon dies right after spawning
The daemon can spawn successfully (CreateProcess OK) and then exit immediately — a missing runtime DLL, wrong architecture, corrupt binary, datadir lock, etc. EmbeddedDaemon's crash monitor already builds a detailed reason for this (translated Windows exit code, e.g. "STATUS_DLL_NOT_FOUND — required DLL not found", plus the launch command and a debug.log tail) and stores it in lastError(), but it runs on a background thread and was never shown. The result was the exact symptom users reported: the wallet unpacks dragonxd.exe, looks "stuck connecting", and the node silently dies-and-respawns until it gives up — with no visible reason (manually starting dragonxd works, so the wallet then connects to it). tryConnect now watches the daemon's crash count (on the main thread, where it already logs daemon state) and surfaces each NEW crash's lastError() once, as a sticky error notification, with a concise "Couldn't start dragonxd" status. The counter resets on a successful connect (alongside the daemon's own crash-count reset), so a later crash re-notifies. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -511,6 +511,7 @@ private:
|
||||
// and show the embedded-daemon start failure (binary/params/spawn) only once. Reset on connect.
|
||||
int daemon_wait_attempts_ = 0;
|
||||
bool daemon_start_error_shown_ = false;
|
||||
int daemon_last_seen_crashes_ = 0; // surface each new embedded-daemon crash reason once
|
||||
float loading_timer_ = 0.0f; // spinner animation for loading overlay
|
||||
|
||||
// Current page (sidebar navigation)
|
||||
|
||||
@@ -300,6 +300,21 @@ void App::tryConnect()
|
||||
externalDetected ? "yes" : "no",
|
||||
daemon_controller_->crashCount(),
|
||||
daemon_controller_->lastError().empty() ? "(none)" : daemon_controller_->lastError().c_str());
|
||||
|
||||
// The embedded daemon can spawn successfully and then exit immediately (a missing runtime
|
||||
// DLL, wrong architecture, corrupt binary, datadir lock, …). The crash monitor records a
|
||||
// detailed reason (translated exit code + launch command + debug.log tail) in lastError(),
|
||||
// but it runs on a background thread and was never shown — so the wallet looked like it was
|
||||
// "stuck connecting" while the node silently died-and-respawned. Surface each new crash once.
|
||||
const int crashes = daemon_controller_->crashCount();
|
||||
if (crashes > daemon_last_seen_crashes_) {
|
||||
daemon_last_seen_crashes_ = crashes;
|
||||
const std::string detail = daemon_controller_->lastError();
|
||||
if (!detail.empty()) {
|
||||
connection_status_ = TR("sb_daemon_start_failed");
|
||||
ui::Notifications::instance().error(detail, 30.0f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VERBOSE_LOGF("[connect #%d] No embedded daemon object (use_embedded=%s)\n",
|
||||
attempt, isUsingEmbeddedDaemon() ? "yes" : "no");
|
||||
@@ -449,6 +464,7 @@ void App::onConnected()
|
||||
state_.daemon_initializing = false; // RPC is answering now; clear the "initializing" overlay
|
||||
daemon_wait_attempts_ = 0; // re-arm the port-busy / start-failure notifications
|
||||
daemon_start_error_shown_ = false;
|
||||
daemon_last_seen_crashes_ = 0; // (onConnected resets the daemon's crash count too)
|
||||
connection_status_ = TR("connected");
|
||||
|
||||
// Reset crash counter on successful connection
|
||||
|
||||
Reference in New Issue
Block a user