Skip to content

Commit ab17006

Browse files
committed
Handle listen errors and lock-meta rejections without leaking the lock
Address Copilot review on PR #24: - Register `server.once('error', ...)` before `listen()` so EADDRINUSE / EACCES releases the proper-lockfile lock and exits, instead of letting Node emit an unhandled error event while still holding it. - Make the `listen()` callback synchronous and chain `writeLockMeta` with `.then()/.catch()` so a rejection (permission/disk error) cannot escape as an unhandled promise rejection — on failure we release the lock and exit with status 1.
1 parent 256d4f4 commit ab17006

1 file changed

Lines changed: 29 additions & 9 deletions

File tree

src/server/index.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,36 @@ async function main(): Promise<void> {
5959
});
6060
});
6161

62-
server.listen(port, '127.0.0.1', async () => {
63-
await writeLockMeta(preflight.lockFilePath, process.pid, port);
64-
const url = `${auth.origin}/?t=${auth.token}`;
65-
log.info('server.listening', { url, pid: process.pid });
66-
console.log(`mdredd listening at ${url}`);
67-
if (shouldOpen) {
68-
open(url).catch((err) => {
69-
console.log(`(could not open browser automatically: ${err.message})`);
62+
// Bind failures (EADDRINUSE/EACCES) surface via the 'error' event, not
63+
// the listen callback. Without this handler Node would emit an unhandled
64+
// 'error' and exit while still holding the proper-lockfile lock,
65+
// blocking restarts until the stale window expires.
66+
server.once('error', (err) => {
67+
console.error(`mdredd: failed to bind ${port}: ${err.message}`);
68+
log.error('server.listen-error', { port, error: err.message });
69+
void preflight.releaseLock().finally(() => process.exit(1));
70+
});
71+
72+
server.listen(port, '127.0.0.1', () => {
73+
// Keep this callback synchronous so a writeLockMeta rejection cannot
74+
// escape as an unhandled promise rejection. On failure release the
75+
// lock (we just acquired it but never wrote the sidecar) and exit.
76+
writeLockMeta(preflight.lockFilePath, process.pid, port)
77+
.then(() => {
78+
const url = `${auth.origin}/?t=${auth.token}`;
79+
log.info('server.listening', { url, pid: process.pid });
80+
console.log(`mdredd listening at ${url}`);
81+
if (shouldOpen) {
82+
open(url).catch((err) => {
83+
console.log(`(could not open browser automatically: ${err.message})`);
84+
});
85+
}
86+
})
87+
.catch((err) => {
88+
console.error(`mdredd: could not write lock metadata: ${(err as Error).message}`);
89+
log.error('server.lock-meta-failed', { error: (err as Error).message });
90+
void preflight.releaseLock().finally(() => process.exit(1));
7091
});
71-
}
7292
});
7393

7494
// 5s for runners to drain (each runner self-bounds at SIGTERM+2s SIGKILL),

0 commit comments

Comments
 (0)