fix: use safeUnlinkQuiet in shutdown and cleanup paths

Shutdown, emergency cleanup, and disconnect paths should never throw
on file deletion failures. Switched from safeUnlink (throws on EPERM)
to safeUnlinkQuiet (swallows all errors) in these best-effort paths.
Normal operation paths (startup, lock release) keep safeUnlink.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-04-09 05:17:54 -10:00
parent 6a857d41ba
commit ad38e006f6
2 changed files with 11 additions and 11 deletions
+5 -5
View File
@@ -11,7 +11,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { safeUnlink, safeKill, isProcessAlive } from './error-handling';
import { safeUnlink, safeUnlinkQuiet, safeKill, isProcessAlive } from './error-handling';
import { resolveConfig, ensureStateDir, readVersionHash } from './config';
const config = resolveConfig();
@@ -812,11 +812,11 @@ Refs: After 'snapshot', use @e1, @e2... as selectors:
// Clean up Chromium profile locks (can persist after crashes)
for (const lockFile of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) {
safeUnlink(path.join(profileDir, lockFile));
safeUnlinkQuiet(path.join(profileDir, lockFile));
}
// Delete stale state file
safeUnlink(config.stateFile);
safeUnlinkQuiet(config.stateFile);
console.log('Launching headed Chromium with extension + sidebar agent...');
try {
@@ -945,9 +945,9 @@ Refs: After 'snapshot', use @e1, @e2... as selectors:
// Clean profile locks and state file
const profileDir = path.join(process.env.HOME || '/tmp', '.gstack', 'chromium-profile');
for (const lockFile of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) {
safeUnlink(path.join(profileDir, lockFile));
safeUnlinkQuiet(path.join(profileDir, lockFile));
}
safeUnlink(config.stateFile);
safeUnlinkQuiet(config.stateFile);
console.log('Disconnected (server was unresponsive — force cleaned).');
process.exit(0);
}
+6 -6
View File
@@ -38,7 +38,7 @@ import { emitActivity, subscribe, getActivityAfter, getActivityHistory, getSubsc
import { inspectElement, modifyStyle, resetModifications, getModificationHistory, detachSession, type InspectorResult } from './cdp-inspector';
// Bun.spawn used instead of child_process.spawn (compiled bun binaries
// fail posix_spawn on all executables including /bin/bash)
import { safeUnlink, safeKill } from './error-handling';
import { safeUnlink, safeUnlinkQuiet, safeKill } from './error-handling';
import * as fs from 'fs';
import * as net from 'net';
import * as path from 'path';
@@ -1188,11 +1188,11 @@ async function shutdown() {
// Clean up Chromium profile locks (prevent SingletonLock on next launch)
const profileDir = path.join(process.env.HOME || '/tmp', '.gstack', 'chromium-profile');
for (const lockFile of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) {
safeUnlink(path.join(profileDir, lockFile));
safeUnlinkQuiet(path.join(profileDir, lockFile));
}
// Clean up state file
safeUnlink(config.stateFile);
safeUnlinkQuiet(config.stateFile);
process.exit(0);
}
@@ -1204,7 +1204,7 @@ process.on('SIGINT', shutdown);
// Defense-in-depth — primary cleanup is the CLI's stale-state detection via health check.
if (process.platform === 'win32') {
process.on('exit', () => {
safeUnlink(config.stateFile);
safeUnlinkQuiet(config.stateFile);
});
}
@@ -1223,9 +1223,9 @@ function emergencyCleanup() {
// Clean Chromium profile locks
const profileDir = path.join(process.env.HOME || '/tmp', '.gstack', 'chromium-profile');
for (const lockFile of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) {
safeUnlink(path.join(profileDir, lockFile));
safeUnlinkQuiet(path.join(profileDir, lockFile));
}
safeUnlink(config.stateFile);
safeUnlinkQuiet(config.stateFile);
}
process.on('uncaughtException', (err) => {
console.error('[browse] FATAL uncaught exception:', err.message);