From 4eee6508ec280d67e13b4a80b370d353325c92ef Mon Sep 17 00:00:00 2001 From: Arav menon Date: Thu, 17 Sep 2026 11:16:06 +0530 Subject: [PATCH 1/5] fix: catch missing DB tables error with clear message in anonymous auth Detect Postgres error code 42P01 (undefined_table) in ensureAnonymousUserExists() and throw an actionable error message instead of a generic internal server error. This helps developers quickly identify that database migrations need to be run. --- apps/sim/lib/auth/anonymous.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/sim/lib/auth/anonymous.ts b/apps/sim/lib/auth/anonymous.ts index c4be061bea0..7848ac7cb23 100644 --- a/apps/sim/lib/auth/anonymous.ts +++ b/apps/sim/lib/auth/anonymous.ts @@ -9,6 +9,19 @@ const logger = createLogger('AnonymousAuth') let anonymousUserEnsured = false +function isMissingTableError(error: unknown): boolean { + if (error && typeof error === 'object') { + const code = (error as Record).code + if (code === '42P01') return true + const cause = (error as Record).cause + if (cause && typeof cause === 'object') { + const innerCode = (cause as Record).code + if (innerCode === '42P01') return true + } + } + return false +} + /** * Ensures the anonymous user and their stats record exist in the database. * Called when DISABLE_AUTH is enabled to ensure DB operations work. @@ -47,6 +60,11 @@ export async function ensureAnonymousUserExists(): Promise { anonymousUserEnsured = true } catch (error) { + if (isMissingTableError(error)) { + throw new Error( + 'Database tables not found. Run database migrations before starting the app: bun run db:migrate' + ) + } if ( error instanceof Error && (error.message.includes('unique') || error.message.includes('duplicate')) From c155996ff63c50fdc0612dfd08f1ecf604c5d9aa Mon Sep 17 00:00:00 2001 From: Arav menon Date: Thu, 17 Sep 2026 11:16:11 +0530 Subject: [PATCH 2/5] fix: prevent OTel timeout from killing dev server - Add DISABLE_TELEMETRY env var to skip all OTel initialization - Reduce export timeout from 30s to 10s for faster failure - Wrap sdk.shutdown() with 5s timeout to prevent blocking exit - Change process.on to process.once for SIGTERM/SIGINT handlers to prevent duplicate execution during HMR reloads --- apps/sim/instrumentation-node.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/sim/instrumentation-node.ts b/apps/sim/instrumentation-node.ts index d9a3668d304..9a3194f03cb 100644 --- a/apps/sim/instrumentation-node.ts +++ b/apps/sim/instrumentation-node.ts @@ -35,7 +35,7 @@ const DEFAULT_TELEMETRY_CONFIG = { maxQueueSize: 2048, maxExportBatchSize: 512, scheduledDelayMillis: 5000, - exportTimeoutMillis: 30000, + exportTimeoutMillis: 10000, }, } @@ -151,8 +151,12 @@ class MothershipOriginSpanProcessor implements SpanProcessor { async function initializeOpenTelemetry() { try { - if (env.NEXT_TELEMETRY_DISABLED === '1' || process.env.NEXT_TELEMETRY_DISABLED === '1') { - logger.info('OpenTelemetry disabled via NEXT_TELEMETRY_DISABLED=1') + if ( + process.env.DISABLE_TELEMETRY === '1' || + env.NEXT_TELEMETRY_DISABLED === '1' || + process.env.NEXT_TELEMETRY_DISABLED === '1' + ) { + logger.info('OpenTelemetry disabled via env var') return } @@ -359,15 +363,19 @@ async function initializeOpenTelemetry() { const shutdownOtel = async () => { try { - await sdk.shutdown() + const shutdownPromise = sdk.shutdown() + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('OTel shutdown timed out')), 5000) + ) + await Promise.race([shutdownPromise, timeoutPromise]) logger.info('OpenTelemetry SDK shut down successfully') } catch (err) { logger.error('Error shutting down OpenTelemetry SDK', err) } } - process.on('SIGTERM', shutdownOtel) - process.on('SIGINT', shutdownOtel) + process.once('SIGTERM', shutdownOtel) + process.once('SIGINT', shutdownOtel) logger.info('OpenTelemetry instrumentation initialized', { serviceName: telemetryConfig.serviceName, @@ -398,8 +406,8 @@ export async function register() { } } - process.on('SIGTERM', shutdownPostHog) - process.on('SIGINT', shutdownPostHog) + process.once('SIGTERM', shutdownPostHog) + process.once('SIGINT', shutdownPostHog) const { startMemoryTelemetry } = await import('./lib/monitoring/memory-telemetry') startMemoryTelemetry() From 393ef652fc70c898a9bbb686de881130c50dbc28 Mon Sep 17 00:00:00 2001 From: Arav menon Date: Thu, 17 Sep 2026 11:16:15 +0530 Subject: [PATCH 3/5] fix: reduce OTel batch export timeout from 30s to 10s Match the default config in instrumentation-node.ts to prevent long shutdown hangs when the telemetry endpoint is unreachable. --- apps/sim/telemetry.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/telemetry.config.ts b/apps/sim/telemetry.config.ts index 0256b3f32db..533253e2a75 100644 --- a/apps/sim/telemetry.config.ts +++ b/apps/sim/telemetry.config.ts @@ -57,7 +57,7 @@ const config = { maxQueueSize: 2048, maxExportBatchSize: 512, scheduledDelayMillis: 5000, - exportTimeoutMillis: 30000, + exportTimeoutMillis: 10000, }, /** From 90753e4855b3591f6c377647319733a51f577e52 Mon Sep 17 00:00:00 2001 From: Arav menon Date: Thu, 17 Sep 2026 11:16:24 +0530 Subject: [PATCH 4/5] docs: add DISABLE_TELEMETRY env var to .env.example Document the new env var that disables all OpenTelemetry collection, preventing export timeouts when the telemetry endpoint is unreachable. --- apps/sim/.env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/sim/.env.example b/apps/sim/.env.example index f5d05f0ac23..8723718c5fd 100644 --- a/apps/sim/.env.example +++ b/apps/sim/.env.example @@ -96,6 +96,7 @@ CRON_SECRET=your_cron_secret # Use `openssl rand -hex 32` to generate. Authentic # LITELLM_BASE_URL=http://localhost:4000 # Base URL for your LiteLLM proxy (OpenAI-compatible) # LITELLM_API_KEY= # Optional bearer token if your LiteLLM proxy requires auth # OPENROUTER_API_KEY= # Optional self-hosted fallback for OpenAI knowledge-base embeddings +# DISABLE_TELEMETRY=1 # Disable all OpenTelemetry collection (traces, metrics, logs). Prevents export timeouts when the telemetry endpoint is unreachable # NEXT_PUBLIC_FORCE_HOSTED=true # Dev only: treat this instance as hosted Sim (sim-auto pool, platform keys); ignored in production builds # FIREWORKS_API_KEY= # Optional Fireworks AI API key for model listing and inference # FIREWORKS_API_KEY_1= # Optional Fireworks API key for rotation (hosted deployments) From d131229df4b2abaa38aa75e45c1f05064905e8fb Mon Sep 17 00:00:00 2001 From: Arav menon Date: Thu, 17 Sep 2026 11:16:28 +0530 Subject: [PATCH 5/5] fix: replace renameSync with cpSync to prevent EXDEV on Ubuntu renameSync fails with 'EXDEV: cross-device link not permitted' when moving files from /tmp (tmpfs) to the project directory (ext4) on Ubuntu. Use cpSync with recursive and force options instead, which works across filesystem boundaries. --- apps/desktop/scripts/ensure-pty-prebuilds.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/scripts/ensure-pty-prebuilds.ts b/apps/desktop/scripts/ensure-pty-prebuilds.ts index a08856a51f2..59de0270cb5 100644 --- a/apps/desktop/scripts/ensure-pty-prebuilds.ts +++ b/apps/desktop/scripts/ensure-pty-prebuilds.ts @@ -16,11 +16,11 @@ import { execFileSync } from 'node:child_process' import { createHash, timingSafeEqual } from 'node:crypto' import { + cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, - renameSync, rmSync, writeFileSync, } from 'node:fs' @@ -103,7 +103,7 @@ async function fetchPrebuild(arch: string, version: string): Promise { const target = packageDir(arch) mkdirSync(dirname(target), { recursive: true }) rmSync(target, { recursive: true, force: true }) - renameSync(join(staging, 'package'), target) + cpSync(join(staging, 'package'), target, { recursive: true, force: true }) } finally { rmSync(staging, { recursive: true, force: true }) }