Skip to content

Commit 9b10d3b

Browse files
cameroncookecodex
andcommitted
fix(daemon): Handle startup and forced shutdown races
Release the startup registry lock and exit non-zero when the daemon server fails to listen. Guard shutdown cleanup so forced timeout and server close cannot run cleanup twice. Co-Authored-By: OpenAI Codex <noreply@openai.com>
1 parent 0047a48 commit 9b10d3b

1 file changed

Lines changed: 31 additions & 16 deletions

File tree

src/daemon.ts

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -333,29 +333,34 @@ async function main(): Promise<void> {
333333
},
334334
});
335335

336-
let forcedShutdownTimer: NodeJS.Timeout | null = setTimeout(() => {
337-
forcedShutdownTimer = null;
338-
log('warn', '[Daemon] Forced shutdown after timeout');
339-
void cleanupArtifacts().finally(() => {
340-
void flushAndCloseSentry(1000).finally(() => {
341-
process.exit(1);
342-
});
343-
});
344-
}, 5000);
345-
forcedShutdownTimer.unref?.();
346-
347-
server.close(() => {
336+
let cleanupStarted = false;
337+
let forcedShutdownTimer: NodeJS.Timeout | null = null;
338+
const finishShutdown = (finalExitCode: number, flushTimeoutMs: number): void => {
339+
if (cleanupStarted) {
340+
return;
341+
}
342+
cleanupStarted = true;
348343
if (forcedShutdownTimer) {
349344
clearTimeout(forcedShutdownTimer);
350345
forcedShutdownTimer = null;
351346
}
352-
log('info', '[Daemon] Server closed');
353347
void cleanupArtifacts().finally(() => {
354348
log('info', '[Daemon] Cleanup complete');
355-
void flushAndCloseSentry(2000).finally(() => {
356-
process.exit(exitCode);
349+
void flushAndCloseSentry(flushTimeoutMs).finally(() => {
350+
process.exit(finalExitCode);
357351
});
358352
});
353+
};
354+
355+
forcedShutdownTimer = setTimeout(() => {
356+
log('warn', '[Daemon] Forced shutdown after timeout');
357+
finishShutdown(1, 1000);
358+
}, 5000);
359+
forcedShutdownTimer.unref?.();
360+
361+
server.close(() => {
362+
log('info', '[Daemon] Server closed');
363+
finishShutdown(exitCode, 2000);
359364
});
360365
};
361366

@@ -417,9 +422,19 @@ async function main(): Promise<void> {
417422
idleCheckTimer.unref?.();
418423
}
419424

420-
server.on('error', releaseStartupRegistryLock);
425+
const handleStartupServerError = (error: Error): void => {
426+
releaseStartupRegistryLock();
427+
const message = error.message;
428+
log('error', `[Daemon] Server startup error: ${message}`, { sentry: true });
429+
console.error('Daemon error:', message);
430+
void flushAndCloseSentry(2000).finally(() => {
431+
process.exit(1);
432+
});
433+
};
434+
server.once('error', handleStartupServerError);
421435

422436
server.listen(socketPath, () => {
437+
server.off('error', handleStartupServerError);
423438
log('info', `[Daemon] Listening on ${socketPath}`);
424439

425440
// Write registry entry after successful listen

0 commit comments

Comments
 (0)