fix(error-handling): surface silent errors across the codebase (#326)

Add console.warn/console.error logging to 22 silent catch blocks across
10 files. Errors in cleanup, migrations, SSO, fleet snapshots, shutdown,
and validation are now visible in logs. ENOENT guards added to
file-system catches to distinguish missing files from permission errors.
No control flow changes.
This commit is contained in:
Anso
2026-04-01 21:56:41 -04:00
committed by GitHub
parent eb9921e678
commit 10597d213a
11 changed files with 97 additions and 38 deletions
+4 -1
View File
@@ -99,7 +99,10 @@ export class ComposeService {
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
});
} finally {
try { fs.unlinkSync(configPath); fs.rmdirSync(tmpDir); } catch { /* best-effort cleanup */ }
try { fs.unlinkSync(configPath); fs.rmdirSync(tmpDir); } catch (e) {
// Best-effort cleanup: temp config dir may already be removed or locked
console.warn('[ComposeService] Could not clean up temp Docker config dir:', (e as Error).message);
}
}
}
+2 -1
View File
@@ -24,8 +24,9 @@ export class CryptoService {
console.warn(`[CryptoService] Fixing permissive key file permissions (was 0o${mode.toString(8)}, set to 0o600)`);
fs.chmodSync(keyPath, 0o600);
}
} catch {
} catch (e) {
// chmod not supported on this platform (e.g. Windows) — skip
console.warn('[CryptoService] Could not enforce key file permissions (platform may not support chmod):', (e as Error).message);
}
} else {
this.key = crypto.randomBytes(KEY_LENGTH);
+15 -3
View File
@@ -436,7 +436,12 @@ export class DatabaseService {
// Drop legacy SSH/TLS columns from pre-0.7 databases (no longer read or written)
const legacyCols = ['host', 'port', 'ssh_port', 'ssh_user', 'ssh_password', 'ssh_key', 'tls_ca', 'tls_cert', 'tls_key'];
for (const col of legacyCols) {
try { this.db.prepare(`ALTER TABLE nodes DROP COLUMN ${col}`).run(); } catch { /* already dropped or never existed */ }
try { this.db.prepare(`ALTER TABLE nodes DROP COLUMN ${col}`).run(); } catch (e: unknown) {
// Expected: column already dropped or never existed
if (!String((e as Error)?.message).includes('no such column')) {
console.warn(`[DatabaseService] Unexpected error dropping legacy column "${col}":`, (e as Error).message);
}
}
}
// Initialize default global settings if they don't exist
@@ -511,7 +516,12 @@ export class DatabaseService {
private migrateSSOColumns(): void {
const maybeAddCol = (table: string, col: string, def: string) => {
try { this.db.prepare(`ALTER TABLE ${table} ADD COLUMN ${col} ${def}`).run(); } catch { /* already exists */ }
try { this.db.prepare(`ALTER TABLE ${table} ADD COLUMN ${col} ${def}`).run(); } catch (e: unknown) {
// Expected: column already exists
if (!String((e as Error)?.message).includes('duplicate column')) {
console.warn(`[DatabaseService] Unexpected error adding column "${col}" to "${table}":`, (e as Error).message);
}
}
};
maybeAddCol('users', 'auth_provider', "TEXT NOT NULL DEFAULT 'local'");
maybeAddCol('users', 'provider_id', 'TEXT DEFAULT NULL');
@@ -562,7 +572,9 @@ export class DatabaseService {
`);
try {
this.db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_role_assignments_unique ON role_assignments(user_id, role, resource_type, resource_id)');
} catch { /* already exists */ }
} catch (e) {
console.warn('[DatabaseService] Could not create role_assignments unique index:', (e as Error).message);
}
}
// --- Agents ---
+13 -7
View File
@@ -651,7 +651,10 @@ class DockerController {
// Destroy the Docker stats stream when the WebSocket closes to prevent
// orphaned streams polling the daemon after client disconnect.
ws.on('close', () => {
try { (stats as any).destroy(); } catch { /* stream already ended */ }
try { (stats as any).destroy(); } catch (e) {
// Stream already ended before client disconnected
console.warn('[DockerController] Stats stream already ended on WS close:', (e as Error).message);
}
});
}
@@ -723,8 +726,9 @@ class DockerController {
case 'resize':
if (msg.rows && msg.cols) {
exec.resize({ h: msg.rows, w: msg.cols }).catch(() => {
// Ignore resize errors (exec may have ended)
exec.resize({ h: msg.rows, w: msg.cols }).catch((e: Error) => {
// Exec may have ended before resize completes
console.warn('[DockerController] Exec resize failed (exec may have ended):', e.message);
});
}
break;
@@ -733,8 +737,9 @@ class DockerController {
// Keep-alive, no-op
break;
}
} catch {
// Non-JSON or malformed message - ignore
} catch (e) {
// Non-JSON or malformed WebSocket message
console.warn('[DockerController] Ignoring malformed exec WS message:', (e as Error).message);
}
});
@@ -742,8 +747,9 @@ class DockerController {
ws.on('close', () => {
try {
stream.destroy();
} catch {
// Ignore destroy errors
} catch (e) {
// Stream already destroyed before WS close
console.warn('[DockerController] Exec stream already destroyed on WS close:', (e as Error).message);
}
});
+17 -7
View File
@@ -275,8 +275,11 @@ export class FileSystemService {
try {
await fsPromises.access(oldEnvPath);
await fsPromises.rename(oldEnvPath, newEnvPath);
} catch {
// No env file to migrate
} catch (e: unknown) {
const code = (e as NodeJS.ErrnoException)?.code;
if (code !== 'ENOENT') {
console.warn(`[FileSystemService] Could not migrate env file for ${stackName}:`, (e as Error).message);
}
}
}
@@ -300,8 +303,11 @@ export class FileSystemService {
try {
await fsPromises.access(src);
await fsPromises.copyFile(src, path.join(backupDir, file));
} catch {
// File doesn't exist, skip
} catch (e: unknown) {
const code = (e as NodeJS.ErrnoException)?.code;
if (code !== 'ENOENT') {
console.warn(`[FileSystemService] Could not back up ${file}:`, (e as Error).message);
}
}
}
@@ -310,8 +316,11 @@ export class FileSystemService {
try {
await fsPromises.access(envSrc);
await fsPromises.copyFile(envSrc, path.join(backupDir, '.env'));
} catch {
// No .env to backup
} catch (e: unknown) {
const code = (e as NodeJS.ErrnoException)?.code;
if (code !== 'ENOENT') {
console.warn('[FileSystemService] Could not back up .env:', (e as Error).message);
}
}
// Write timestamp marker
@@ -343,7 +352,8 @@ export class FileSystemService {
try {
const ts = await fsPromises.readFile(tsFile, 'utf-8');
return { exists: true, timestamp: parseInt(ts, 10) || null };
} catch {
} catch (e) {
console.warn('[FileSystemService] Backup timestamp file unreadable:', (e as Error).message);
return { exists: true, timestamp: null };
}
} catch {
+2 -1
View File
@@ -7,7 +7,8 @@ function getUnixShell() {
try {
execSync('which bash', { stdio: 'ignore' });
return 'bash';
} catch {
} catch (e) {
console.warn('[HostTerminalService] bash not found, falling back to sh:', (e as Error).message);
return 'sh';
}
}
+3 -1
View File
@@ -180,7 +180,9 @@ export class MonitorService {
totalReclaimableBytes += bytes;
}
}
} catch (e) { }
} catch (e) {
console.warn('[MonitorService] Failed to parse Docker system df output:', e);
}
}
const reclaimGb = totalReclaimableBytes / (1024 * 1024 * 1024);