Better error logging for non-Administrator/headless server scenarios

Three improvements for users running on servers where the Windows account
is not "Administrator" or where the environment is headless (Service, RDP-
disconnected, no interactive desktop):

1. readSettingsFile / readSessionFile: distinguish ENOENT (normal first run)
   from EACCES/EPERM (permission problem). The latter logs an explicit
   message including the current Windows username so the user can spot
   misconfigured ACLs immediately.

2. ensureBaseDir: log EACCES/EPERM with the current username before re-
   throwing. Previously the error bubbled up without any hint why the
   AppData directory creation failed.

3. createTray: log a warning when Tray creation fails (typical on Windows
   Service / headless servers / RDP disconnected sessions). Previously the
   error was silently swallowed and minimize-to-tray would just not work
   without explanation.

These errors were silently swallowed before, making it impossible to
diagnose problems on servers with restricted user accounts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe 2026-04-19 11:53:07 +02:00
parent f44a321e74
commit d4b98ad172
2 changed files with 139 additions and 111 deletions

View File

@ -136,7 +136,11 @@ function createTray(): void {
const iconPath = path.join(app.getAppPath(), "assets", "app_icon.ico"); const iconPath = path.join(app.getAppPath(), "assets", "app_icon.ico");
try { try {
tray = new Tray(iconPath); tray = new Tray(iconPath);
} catch { } catch (error) {
// Fails on headless servers / Windows Service / RDP-disconnected sessions.
// Log so a user running on a non-Administrator/headless server can see
// why minimize-to-tray doesn't work, instead of getting an inaccessible window.
logger.warn(`Tray-Icon konnte nicht erstellt werden (Headless/RDP/Service?): ${String(error)} - Minimize-to-Tray steht nicht zur Verfuegung, Fenster bleibt sichtbar.`);
return; return;
} }
tray.setToolTip(APP_NAME); tray.setToolTip(APP_NAME);

View File

@ -530,7 +530,15 @@ export function createStoragePaths(baseDir: string): StoragePaths {
} }
function ensureBaseDir(baseDir: string): void { function ensureBaseDir(baseDir: string): void {
try {
fs.mkdirSync(baseDir, { recursive: true }); fs.mkdirSync(baseDir, { recursive: true });
} catch (error) {
const code = (error as NodeJS.ErrnoException)?.code || "";
if (code === "EACCES" || code === "EPERM") {
logger.error(`AppData-Ordner kann nicht erstellt werden (${code}): ${baseDir} - pruefe Schreibrechte fuer Benutzer ${process.env.USERNAME || process.env.USER || "?"}`);
}
throw error;
}
} }
/** JSON replacer that sanitizes NaN/Infinity to null to prevent file corruption. */ /** JSON replacer that sanitizes NaN/Infinity to null to prevent file corruption. */
@ -556,7 +564,18 @@ function readSettingsFile(filePath: string): AppSettings | null {
...parsed ...parsed
}); });
return sanitizeCredentialPersistence(merged); return sanitizeCredentialPersistence(merged);
} catch { } catch (error) {
// Distinguish permission/access errors from missing/corrupt JSON so a
// misconfigured server (e.g. unusual user, restricted AppData) shows a
// clear log entry instead of silently falling back to defaults.
const code = (error as NodeJS.ErrnoException)?.code || "";
if (code === "ENOENT") {
// file doesn't exist — normal on first run
} else if (code === "EACCES" || code === "EPERM") {
logger.error(`Settings-Datei nicht zugreifbar (${code}): ${filePath} - pruefe Datei-/Ordner-Berechtigungen fuer Benutzer ${process.env.USERNAME || process.env.USER || "?"}`);
} else {
logger.warn(`Settings-Datei nicht lesbar: ${filePath}: ${String(error)}`);
}
return null; return null;
} }
} }
@ -807,7 +826,12 @@ function readSessionFile(filePath: string): SessionState | null {
logger.info(`Session geladen: ${filePath} (${pkgCount} Pakete, ${itemCount} Items)`); logger.info(`Session geladen: ${filePath} (${pkgCount} Pakete, ${itemCount} Items)`);
return session; return session;
} catch (error) { } catch (error) {
const code = (error as NodeJS.ErrnoException)?.code || "";
if (code === "EACCES" || code === "EPERM") {
logger.error(`Session-Datei nicht zugreifbar (${code}): ${filePath} - pruefe Datei-/Ordner-Berechtigungen fuer Benutzer ${process.env.USERNAME || process.env.USER || "?"}`);
} else {
logger.error(`Session-Datei nicht lesbar: ${filePath}: ${String(error)}`); logger.error(`Session-Datei nicht lesbar: ${filePath}: ${String(error)}`);
}
return null; return null;
} }
} }