Error capture
Normalising errors and reporting them to PostHog.
Errors are normalised through a single helper, parseError, which returns a
message you can show a user and reports the error in the background.
import { parseError } from '@kreogen/observability/error';
try {
await doSomething();
} catch (error) {
const message = parseError(error);
return { error: message };
}parseError never throws. Reporting is best-effort — a telemetry outage must
not turn into a user-visible failure — and the message is returned regardless.
In the browser
Errors are captured to PostHog through posthog-js, which is already
initialised by the analytics instrumentation. Nothing extra to configure: if
NEXT_PUBLIC_POSTHOG_KEY is set, exceptions appear in PostHog.
The design system's handleError wraps this and shows a toast:
import { handleError } from '@kreogen/design-system/lib/utils';
<Button onClick={() => doSomething().catch(handleError)}>Save</Button>On the server
Server errors are recorded through the structured logger rather than sent to PostHog directly. In a container, stdout is the log transport — the platform collector picks it up — so the log line is the durable record.
parseError is reachable from client components, so it must never import
anything marked server-only. Reaching for the PostHog Node client here —
even behind a dynamic import — pulls a server-only module into the client
graph and fails the build. This has happened; it typechecks cleanly and only
surfaces at build time.
If you want a specific server-side exception in PostHog, capture it explicitly where the server client is already safe to import:
import { analytics } from '@kreogen/analytics/server';
analytics?.captureException(error);Error boundaries
Each app has a global-error.tsx that reports through parseError and
renders a minimal recovery screen. Route-level error.tsx boundaries can do
the same.