Skip to content

Commit f99aa5f

Browse files
authored
fix: proper catch-all daemon startup errors (#2030)
this should engage isErrored only when ipfs daemon is truly errored: - checks if pid is alive, and shows "startup error" window when process is dead - removes false-negatives caused by migration code or other "soft" errors printed to the console
1 parent d63a689 commit f99aa5f

File tree

4 files changed

+37
-17
lines changed

4 files changed

+37
-17
lines changed

assets/locales/en.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@
231231
"message": "One moment! IPFS Desktop needs to run the latest data store migrations:",
232232
"closeAndContinue": "Continue in the background"
233233
},
234-
"migrationFailedDialog": {
235-
"title": "IPFS Desktop Migration Has Failed",
236-
"message": "IPFS has encountered an error and migration could not be completed:"
234+
"startupFailedDialog": {
235+
"title": "IPFS Desktop Startup Has Failed",
236+
"message": "IPFS node has encountered an error and startup could not be completed:"
237237
}
238238
}

src/daemon/daemon.js

+30-10
Original file line numberDiff line numberDiff line change
@@ -93,24 +93,34 @@ async function startIpfsWithLogs (ipfsd) {
9393
let isMigrating, isErrored, isFinished
9494
let logs = ''
9595

96+
const isSpawnedDaemonDead = (ipfsd) => {
97+
if (typeof ipfsd.subprocess === 'undefined') throw new Error('undefined ipfsd.subprocess, unable to reason about startup errors')
98+
if (ipfsd.subprocess === null) return false // not spawned yet or remote
99+
if (ipfsd.subprocess?.failed) return true // explicit failure
100+
101+
// detect when spawned ipfsd process is gone/dead
102+
// by inspecting its pid - it should be alive
103+
const { pid } = ipfsd.subprocess
104+
try {
105+
// signal 0 throws if process is missing, noop otherwise
106+
process.kill(pid, 0)
107+
return false
108+
} catch (e) {
109+
return true
110+
}
111+
}
112+
96113
const stopListening = listenToIpfsLogs(ipfsd, data => {
97114
logs += data.toString()
98115
const line = data.toLowerCase()
99116
isMigrating = isMigrating || line.includes('migration')
100-
isErrored = isErrored || line.includes('error')
117+
isErrored = isErrored || isSpawnedDaemonDead(ipfsd)
101118
isFinished = isFinished || line.includes('daemon is ready')
102119

103-
if (!isMigrating) {
120+
if (!isMigrating && !isErrored) {
104121
return
105122
}
106123

107-
// Undo error state if retrying after HTTP failure
108-
// https://github.com/ipfs/ipfs-desktop/issues/2003
109-
if (isErrored && line.includes('fetching with ipfs') && !line.includes('error')) {
110-
isErrored = false
111-
if (migrationPrompt) migrationPrompt.loadWindow(logs, isErrored, isFinished)
112-
}
113-
114124
if (!migrationPrompt) {
115125
logger.info('[daemon] ipfs data store is migrating')
116126
migrationPrompt = showMigrationPrompt(logs, isErrored, isFinished)
@@ -133,10 +143,20 @@ async function startIpfsWithLogs (ipfsd) {
133143
} catch (e) {
134144
err = e
135145
} finally {
136-
// stop monitoring daemon output - we only care about migration phase
146+
// stop monitoring daemon output - we only care about startup phase
137147
stopListening()
148+
149+
// Show startup error using the same UI as migrations.
150+
// This is catch-all that will show stdout/stderr of ipfs daemon
151+
// that failed to start, allowing user to self-diagnose or report issue.
152+
isErrored = isErrored || isSpawnedDaemonDead(ipfsd)
138153
if (isErrored) { // save daemon output to error.log
139154
logger.error(logs)
155+
if (migrationPrompt) {
156+
migrationPrompt.loadWindow(logs, isErrored, isFinished)
157+
} else {
158+
showMigrationPrompt(logs, isErrored, isFinished)
159+
}
140160
}
141161
}
142162

src/daemon/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ module.exports = async function (ctx) {
5353

5454
ipfsd = res.ipfsd
5555

56-
logger.info(`[daemon] PeerID is ${res.id}`)
57-
logger.info(`[daemon] Repo is at ${ipfsd.path}`)
56+
logger.info(`[daemon] IPFS_PATH: ${ipfsd.path}`)
57+
logger.info(`[daemon] PeerID: ${res.id}`)
5858

5959
// Update the path if it was blank previously.
6060
// This way we use the default path when it is

src/daemon/migration-prompt.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ const inProgressTemplate = (logs, id, done) => {
6464
}
6565

6666
const errorTemplate = (logs) => {
67-
const title = i18n.t('migrationFailedDialog.title')
68-
const message = i18n.t('migrationFailedDialog.message')
67+
const title = i18n.t('startupFailedDialog.title')
68+
const message = i18n.t('startupFailedDialog.message')
6969
const buttons = [
7070
`<button class="default" onclick="javascript:window.close()">${i18n.t('close')}</button>`,
7171
`<button onclick="javascript:openIssue()">${i18n.t('reportTheError')}</button>`

0 commit comments

Comments
 (0)