Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/error-log-raw-err-convention.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inkeep/open-knowledge": patch
---

Error and warn log lines across the server, CLI, and desktop main process now attach the raw error under the `err` key, so on-disk JSONL logs (what bug-report bundles collect) carry the full name/message/stack instead of a pre-stringified message with no stack. API error log lines additionally carry the request's `x-request-id` for correlation with the access log and client reports, the MCP stdio logger serializes Error values instead of flattening them to `{}`, and the desktop root logger gained explicit `err` serializers. No wire-shape changes.
5 changes: 1 addition & 4 deletions packages/cli/src/commands/auth/git-credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,7 @@ export function gitCredentialCommand(
// A throw from getTokenStore / a callback / handleCredentialGet must
// not skip the flush — that's the exact failure (a vanished credential)
// we need persisted. Log and flush before exiting non-zero.
log?.error(
{ error: err instanceof Error ? err.message : String(err) },
'[auth] git-credential get: unexpected error',
);
log?.error({ err }, '[auth] git-credential get: unexpected error');
await flushFileLogger(log);
process.exit(1);
}
Expand Down
7 changes: 2 additions & 5 deletions packages/cli/src/commands/bug-report-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function resolveProjectSlug(cwd: string, logger?: BundleLogger): string |
// (or null), but log it so a missing/wrong project slug in the bundle is
// diagnosable rather than silent — same rationale as resolveContentDir.
logger?.warn(
{ configPath, err: err instanceof Error ? err.message : String(err) },
{ configPath, err },
'bug-report: failed to read .ok/config.yml for project slug; using path-hash fallback',
);
}
Expand Down Expand Up @@ -248,10 +248,7 @@ function addContentFiles(args: {
// A file we listed but can't read is dropped rather than aborting the
// whole report; log it so the omission is diagnosable — the bundled
// MANIFEST lists only what was written, never what was skipped.
args.logger?.warn(
{ file, prefix: args.prefix, err: err instanceof Error ? err.message : String(err) },
'bug-report: skipped unreadable file',
);
args.logger?.warn({ file, prefix: args.prefix, err }, 'bug-report: skipped unreadable file');
}
}
}
Expand Down
15 changes: 3 additions & 12 deletions packages/cli/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,24 +594,15 @@ export function buildIdleShutdownHandler(
'idle-shutdown: SIGTERM grace expired — escalated to SIGKILL',
);
} catch (err) {
input.log?.error(
{ pid: lock.pid, err: err instanceof Error ? err.message : String(err) },
'idle-shutdown: SIGKILL failed',
);
input.log?.error({ pid: lock.pid, err }, 'idle-shutdown: SIGKILL failed');
}
}
} catch (err) {
input.log?.warn(
{ pid: lock.pid, err: err instanceof Error ? err.message : String(err) },
'idle-shutdown: failed to SIGTERM UI sibling',
);
input.log?.warn({ pid: lock.pid, err }, 'idle-shutdown: failed to SIGTERM UI sibling');
}
}
} catch (err) {
input.log?.warn(
{ err: err instanceof Error ? err.message : String(err) },
'idle-shutdown: UI lookup failed; proceeding with destroy',
);
input.log?.warn({ err }, 'idle-shutdown: UI lookup failed; proceeding with destroy');
}
await input.destroy();
};
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/report-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function resolveContentDir(projectDir: string, logger?: BundleLogger): string {
// root (the bundle should still succeed), but log it so a wrong
// content-dir in the resulting bundle is diagnosable, not silent.
logger?.warn(
{ configPath, err: err instanceof Error ? err.message : String(err) },
{ configPath, err },
'bug-report: failed to read .ok/config.yml; falling back to project root as content dir',
);
}
Expand Down
29 changes: 13 additions & 16 deletions packages/desktop/src/main/auto-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
// consistent (fallback-attempted, no re-check) state instead.
logger.error('proxy-feed fallback setFeedURL threw', {
cause,
message: err instanceof Error ? err.message : String(err),
err,
});
return;
}
Expand All @@ -680,7 +680,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
// right after the proxy one is operationally relevant, not debug noise.
const ctx = {
code: err?.code,
message: err instanceof Error ? err.message : String(err),
err,
};
if (isClassifiedUpdaterError(err)) {
logger.warn('post-fallback checkForUpdates rejected', ctx);
Expand Down Expand Up @@ -754,7 +754,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
} catch (err) {
logger.error('writeState failed — state gate not armed', {
ctx,
message: err instanceof Error ? err.message : String(err),
err,
});
return false;
}
Expand Down Expand Up @@ -937,7 +937,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
const logFn = isClassifiedUpdaterError(err) ? logger.warn : logger.debug;
logFn('check-now checkForUpdates rejected', {
code,
message: err instanceof Error ? err.message : String(err),
err,
timestamp: now().toISOString(),
});
// The synchronous-reject path is rare (electron-updater normally emits
Expand Down Expand Up @@ -1013,8 +1013,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
const logFn = isClassifiedUpdaterError(err) ? logger.warn : logger.debug;
logFn('downloadUpdate rejected', {
code,
message: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
err,
timestamp: now().toISOString(),
});
});
Expand Down Expand Up @@ -1130,14 +1129,13 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
if (isClassifiedUpdaterError(err)) {
logger.warn('error (classified)', {
code: err.code,
message: err.message,
err,
timestamp: now().toISOString(),
});
onDispatch?.('error-classified');
} else {
logger.error('error (unclassified)', {
message: err.message,
stack: err.stack,
err,
timestamp: now().toISOString(),
});
onDispatch?.('error-unclassified');
Expand Down Expand Up @@ -1268,7 +1266,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
await opts.prepareForRelaunch();
} catch (err) {
logger.warn('prepareForRelaunch threw — proceeding to quitAndInstall anyway', {
message: err instanceof Error ? err.message : String(err),
err,
});
}
}
Expand Down Expand Up @@ -1581,7 +1579,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
// also emits `error` for these, so the catch here is just a defensive
// log. Event handlers run either way.
logger.debug('checkForUpdates rejected', {
message: err instanceof Error ? err.message : String(err),
err,
});
});
scheduleNextCheck();
Expand All @@ -1604,7 +1602,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
})
.catch((err: unknown) => {
logger.debug('first-launch checkForUpdates rejected', {
message: err instanceof Error ? err.message : String(err),
err,
});
// If the proxy feed caused it, revert to GitHub and re-check once.
revertToGithubFeed('first-check-rejected');
Expand Down Expand Up @@ -1661,7 +1659,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
} catch (err) {
logger.warn('updater.off failed during destroy', {
event,
message: err instanceof Error ? err.message : String(err),
err,
});
}
};
Expand All @@ -1678,7 +1676,7 @@ export function startAutoUpdater(opts: StartAutoUpdaterOpts): StartAutoUpdaterHa
} catch (err) {
logger.warn('ipcMain.removeHandler failed during destroy', {
channel,
message: err instanceof Error ? err.message : String(err),
err,
});
}
};
Expand Down Expand Up @@ -1747,8 +1745,7 @@ export async function bootAutoUpdater(
return startAutoUpdater({ updater: autoUpdater, ...opts });
} catch (err) {
logger.error('auto-updater boot failed — app will run without updates this session', {
message: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
err,
});
return null;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/desktop/src/main/branch-info-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export async function proxyFetchBranchInfo(
raw = await res.json();
} catch (err) {
deps.log?.warn('[branch-info-proxy] branch-info fetch failed', {
err: err instanceof Error ? err.message : String(err),
err,
});
return null;
}
Expand Down Expand Up @@ -247,7 +247,7 @@ export async function proxyAwaitBranchSwitched(
}
} catch (err) {
deps.log?.warn('[branch-info-proxy] server-info poll failed (will retry)', {
err: err instanceof Error ? err.message : String(err),
err,
});
}
if (raw !== undefined) {
Expand Down Expand Up @@ -302,7 +302,7 @@ export async function proxyRunCheckout(
raw = await res.json();
} catch (err) {
deps.log?.warn('[branch-info-proxy] checkout fetch failed', {
err: err instanceof Error ? err.message : String(err),
err,
});
return null;
}
Expand Down Expand Up @@ -364,7 +364,7 @@ export async function proxyShareTargetStatus(
raw = await res.json();
} catch (err) {
deps.log?.warn('[branch-info-proxy] target-status fetch failed', {
err: err instanceof Error ? err.message : String(err),
err,
});
return null;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/desktop/src/main/bundle-replace-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export function startBundleReplaceWatcher(
});
} catch (err) {
logger.warn('detector threw', {
err: err instanceof Error ? err.message : String(err),
err,
});
return;
}
Expand Down Expand Up @@ -242,7 +242,7 @@ export function startBundleReplaceWatcher(
// log accumulates one entry per interval — bounded by intervalMs.
if (!stopped) armed = true;
logger.warn('dialog failed, re-armed for next tick', {
err: err instanceof Error ? err.message : String(err),
err,
});
});
};
Expand Down
12 changes: 6 additions & 6 deletions packages/desktop/src/main/consent-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,28 +164,28 @@ export function requestUserConsent(
ipcMain.removeHandler('ok:onboarding:confirm');
} catch (err) {
logger.warn('removeHandler(confirm) threw', {
message: err instanceof Error ? err.message : String(err),
err,
});
}
try {
ipcMain.removeHandler('ok:onboarding:cancel');
} catch (err) {
logger.warn('removeHandler(cancel) threw', {
message: err instanceof Error ? err.message : String(err),
err,
});
}
try {
ipcMain.removeHandler('ok:onboarding:probe-content');
} catch (err) {
logger.warn('removeHandler(probe-content) threw', {
message: err instanceof Error ? err.message : String(err),
err,
});
}
try {
ipcMain.removeHandler('ok:onboarding:renderer-ready');
} catch (err) {
logger.warn('removeHandler(renderer-ready) threw', {
message: err instanceof Error ? err.message : String(err),
err,
});
}
}
Expand Down Expand Up @@ -317,7 +317,7 @@ export function requestUserConsent(
sendToRenderer(event.sender, 'ok:onboarding:show', payload);
} catch (err) {
logger.error('show dispatch failed — handler stays armed for retry', {
message: err instanceof Error ? err.message : String(err),
err,
});
return undefined;
}
Expand Down Expand Up @@ -351,7 +351,7 @@ export function requestUserConsent(
capturedSenderId = navigator.id;
} catch (err) {
logger.error('proactive show dispatch failed — falling back to renderer-ready', {
message: err instanceof Error ? err.message : String(err),
err,
});
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/desktop/src/main/crash-detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ export function createCrashDetection(deps: CrashDetectionDeps): CrashDetection {
{
event: 'crash-detection.sentinel-write-failed',
context,
cause: err instanceof Error ? err.message : String(err),
err,
},
context === 'arm'
? 'could not arm the dirty-shutdown sentinel'
Expand Down Expand Up @@ -315,7 +315,7 @@ export function createCrashDetection(deps: CrashDetectionDeps): CrashDetection {
deps.logger.warn(
{
event: 'crash-detection.store-write-failed',
cause: err instanceof Error ? err.message : String(err),
err,
},
'could not persist crash acknowledgment state',
);
Expand Down Expand Up @@ -546,7 +546,7 @@ export function createCrashDetection(deps: CrashDetectionDeps): CrashDetection {
deps.logger.warn(
{
event: 'crash-detection.sentinel-clear-failed',
cause: err instanceof Error ? err.message : String(err),
err,
},
'could not clear the dirty-shutdown sentinel — next boot may prompt spuriously',
);
Expand Down
4 changes: 4 additions & 0 deletions packages/desktop/src/main/desktop-logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ function getRootLogger(): pino.Logger {
level: resolveLogLevel(),
name: loggerName,
redact: { paths: REDACT_PATHS, censor: '[REDACTED]' },
// Raw Errors land under `err` (convention) — serialize name/message/stack
// explicitly on BOTH keys so a stray `error:` field never flattens an
// Error to `{}` in the JSONL file. Mirrors the server logger's setup.
serializers: { err: pino.stdSerializers.err, error: pino.stdSerializers.err },
base: { pid: process.pid, hostname: undefined, runtime: 'desktop' },
timestamp: pino.stdTimeFunctions.isoTime,
},
Expand Down
4 changes: 2 additions & 2 deletions packages/desktop/src/main/git-preflight-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ async function showUnknownErrorDialog(deps: EnsureGitDeps, err: Error): Promise<
});
} catch (dialogErr) {
deps.log?.warn('ensureGitAvailable: unknown-error dialog failed', {
err: dialogErr instanceof Error ? dialogErr.message : String(dialogErr),
err: dialogErr,
});
}
}
Expand Down Expand Up @@ -201,7 +201,7 @@ export async function ensureGitAvailable(deps: EnsureGitDeps): Promise<EnsureGit
} catch (err) {
deps.log?.warn('ensureGitAvailable: openExternal failed', {
url: currentErr.guidance.url,
err: err instanceof Error ? err.message : String(err),
err,
});
failedInstallUrl = currentErr.guidance.url;
}
Expand Down
Loading