Cosmo Docs
Desktop App

Error Tracking & Telemetry

PostHog-based error capture, analytics events, and feedback pipeline across main and renderer processes

Cosmo uses PostHog for error tracking, analytics, and AI-collected feedback. Instrumentation spans both Electron processes and is silently disabled in local development.

Source files:

  • packages/desktop/src/main/error-tracker.ts — main process
  • packages/desktop/src/renderer/src/lib/posthog.ts — renderer process
  • packages/desktop/src/renderer/src/components/ErrorBoundary.tsx — React error boundary

Activation

PostHog is only active when the API key is present. The host variable is optional (defaults to https://us.i.posthog.com):

Env varDescription
VITE_PUBLIC_POSTHOG_PROJECT_TOKENProject API key
VITE_PUBLIC_POSTHOG_HOSTIngest host (default: https://us.i.posthog.com)

In local development these are not set, so all PostHog calls are no-ops. No analytics data is sent during development.

Main Process (error-tracker.ts)

The main process uses posthog-node (server-side SDK). It is initialised once at app startup by initMainProcessErrorTracking(), which is called from packages/desktop/src/main/index.ts.

Initialisation

initMainProcessErrorTracking()

Creates a PostHog client with:

OptionValue
flushAt5 events
flushInterval5 000 ms

Error Capture

Two Node.js process events are hooked:

EventTrigger
uncaughtExceptionSynchronous exception that escaped all catch blocks
unhandledRejectionPromise rejection with no .catch() handler

Both call captureMainProcessError(error, source), which sends a $exception event to PostHog with these properties:

{
  platform: 'desktop',
  process: 'main',
  source: 'uncaughtException' | 'unhandledRejection',
}

Shutdown

await shutdownErrorTracker()

Called on app.before-quit to flush any buffered events before the process exits.

Renderer Process (lib/posthog.ts)

The renderer uses posthog-js (browser SDK). It is initialised once by initPostHog(), called from packages/desktop/src/renderer/src/main.tsx during app bootstrap.

Initialisation Options

posthog.init(token, {
  api_host: host,
  capture_pageview: false,   // Electron has no URL routing; pages are tracked manually
  persistence: 'localStorage',
})
posthog.register({ platform: 'desktop' })  // super-property on every event

capture_pageview: false prevents PostHog from auto-firing a pageview event when the Electron renderer loads — there is no meaningful URL to track.

The platform: 'desktop' super-property is registered globally so every event carries it for segmentation.

React Error Boundary (ErrorBoundary.tsx)

ErrorBoundary is a React class component wrapping the entire renderer tree. It catches unhandled render errors that escape component try/catch blocks.

Lifecycle

  1. A render error throws inside any descendant component.
  2. React calls getDerivedStateFromError → sets hasError: true.
  3. React calls componentDidCatch(error, info) → sends to PostHog:
posthog.captureException(error, {
  platform: 'desktop',
  componentStack: info.componentStack,
})
  1. The boundary renders a fallback UI: a "Something went wrong" message with a Try again button that resets hasError to false and re-mounts the children.

Theme Analytics Events

ThemeContext emits two events when the user changes appearance settings:

EventPropertiesTrigger
theme_mode_changed{ mode: 'dark' | 'light' }User toggles dark/light mode
theme_preset_changed{ preset: string }User selects a colour preset

Theme state is also registered as a super-property so all events carry the current theme:

posthog.register({ theme_mode: theme, theme_preset: preset })

Feedback Pipeline

AI-collected feedback (from the cosmo_feedback_submit LLM tool) travels from the main process to the renderer for PostHog capture. This routing exists because PostHog survey events must come from the browser SDK to appear in the PostHog Surveys dashboard.

Flow

LLM calls cosmo_feedback_submit

ChatManager (main process) captures feedback input

IPC: posthog:capture-feedback event → renderer

posthog.ts setupFeedbackCapture() listener

PostHog "survey sent" event(s)

Survey Event Structure

Each feedback submission produces up to two PostHog survey sent events:

Rating event (emitted only when importanceRating is provided):

posthog.capture('survey sent', {
  $survey_id: surveyId,
  [`$survey_response_${ratingQuestionId}`]: input.importanceRating,
  $survey_submission_id: submissionId,
  $survey_completed: false,
  feedback_type: input.type,
  feedback_title: input.title,
  capability_area: input.capabilityArea,
})

Text event (always emitted):

posthog.capture('survey sent', {
  $survey_id: surveyId,
  [`$survey_response_${textQuestionId}`]: responseText,
  $survey_submission_id: submissionId,
  $survey_completed: true,
  feedback_type: input.type,
  feedback_title: input.title,
  feedback_description: input.description,
  feedback_context: input.conversationContext,
  capability_area: input.capabilityArea,
  importance_rating: input.importanceRating,
})

responseText is assembled from [type] title + description + context + userNotes, joined with double newlines.

FeedbackCaptureData Type

interface FeedbackCaptureData {
  surveyId: string
  ratingQuestionId: string
  textQuestionId: string
  input: {
    type: string
    title: string
    description: string
    conversationContext?: string
    capabilityArea?: string
    importanceRating?: number
    userNotes?: string
  }
}

Data Sanitization

No explicit PII scrubbing is performed. The following practices limit exposure:

  • conversationContext is an optional field — ChatManager populates it only with a short summary, not the full message history.
  • Error stack traces from componentDidCatch and captureMainProcessError may contain file paths but not user data.
  • The feedback text fields contain only what the user typed or what the AI extracted from the conversation — they never include authentication tokens or credentials.

Summary of All Tracked Events

EventSourceWhen
$exception (main)error-tracker.tsUncaught exception or unhandled rejection in main process
$exception (renderer)ErrorBoundary.tsxUnhandled render error in React tree
theme_mode_changedThemeContext.tsxUser toggles dark/light mode
theme_preset_changedThemeContext.tsxUser changes colour preset
survey sentposthog.tsAI feedback submitted via cosmo_feedback_submit tool