From 12d1bdbae09958bd1194046a85a0a25829eecec2 Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Fri, 30 Jan 2026 09:51:35 -0700 Subject: [PATCH 1/2] chore: treat extension .env as optional Gate dotenvx.config() behind fs.existsSync() to suppress noisy [MISSING_ENV_FILE] console errors when the optional .env file is absent. --- src/__tests__/extension.spec.ts | 33 +++++++++++++++++++++++++++++++++ src/extension.ts | 14 +++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/__tests__/extension.spec.ts b/src/__tests__/extension.spec.ts index 5b072672699..2dd55b89e46 100644 --- a/src/__tests__/extension.spec.ts +++ b/src/__tests__/extension.spec.ts @@ -46,6 +46,11 @@ vi.mock("@dotenvx/dotenvx", () => ({ config: vi.fn(), })) +// Mock fs so the extension module can safely check for optional .env. +vi.mock("fs", () => ({ + existsSync: vi.fn().mockReturnValue(false), +})) + const mockBridgeOrchestratorDisconnect = vi.fn().mockResolvedValue(undefined) const mockCloudServiceInstance = { @@ -238,6 +243,34 @@ describe("extension.ts", () => { authStateChangedHandler = undefined }) + test("does not call dotenvx.config when optional .env does not exist", async () => { + vi.resetModules() + + const fs = await import("fs") + vi.mocked(fs.existsSync).mockReturnValue(false) + + const dotenvx = await import("@dotenvx/dotenvx") + + const { activate } = await import("../extension") + await activate(mockContext) + + expect(dotenvx.config).not.toHaveBeenCalled() + }) + + test("calls dotenvx.config when optional .env exists", async () => { + vi.resetModules() + + const fs = await import("fs") + vi.mocked(fs.existsSync).mockReturnValue(true) + + const dotenvx = await import("@dotenvx/dotenvx") + + const { activate } = await import("../extension") + await activate(mockContext) + + expect(dotenvx.config).toHaveBeenCalledTimes(1) + }) + test("authStateChangedHandler calls BridgeOrchestrator.disconnect when logged-out event fires", async () => { const { CloudService, BridgeOrchestrator } = await import("@roo-code/cloud") diff --git a/src/extension.ts b/src/extension.ts index bcfbe339932..ae8b927cd6d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,15 +1,19 @@ import * as vscode from "vscode" import * as dotenvx from "@dotenvx/dotenvx" +import * as fs from "fs" import * as path from "path" // Load environment variables from .env file try { - // Specify path to .env file in the project root directory + // The extension-level .env is optional (not shipped in production builds). + // Avoid calling dotenvx when the file doesn't exist, otherwise dotenvx emits + // a noisy [MISSING_ENV_FILE] error to the extension host console. const envPath = path.join(__dirname, "..", ".env") - dotenvx.config({ path: envPath }) -} catch (e) { - // Silently handle environment loading errors - console.warn("Failed to load environment variables:", e) + if (fs.existsSync(envPath)) { + dotenvx.config({ path: envPath }) + } +} catch { + // Best-effort only: never fail extension activation due to optional env loading. } import type { CloudUserInfo, AuthState } from "@roo-code/types" From 78bb9458238671d48ed3920015f49b26d88b9e04 Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Fri, 30 Jan 2026 10:18:56 -0700 Subject: [PATCH 2/2] chore: make optional .env loading best-effort --- src/__tests__/extension.spec.ts | 2 ++ src/extension.ts | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/__tests__/extension.spec.ts b/src/__tests__/extension.spec.ts index 2dd55b89e46..0bdbb26d462 100644 --- a/src/__tests__/extension.spec.ts +++ b/src/__tests__/extension.spec.ts @@ -245,6 +245,7 @@ describe("extension.ts", () => { test("does not call dotenvx.config when optional .env does not exist", async () => { vi.resetModules() + vi.clearAllMocks() const fs = await import("fs") vi.mocked(fs.existsSync).mockReturnValue(false) @@ -259,6 +260,7 @@ describe("extension.ts", () => { test("calls dotenvx.config when optional .env exists", async () => { vi.resetModules() + vi.clearAllMocks() const fs = await import("fs") vi.mocked(fs.existsSync).mockReturnValue(true) diff --git a/src/extension.ts b/src/extension.ts index ae8b927cd6d..262bf623378 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,16 +4,17 @@ import * as fs from "fs" import * as path from "path" // Load environment variables from .env file -try { - // The extension-level .env is optional (not shipped in production builds). - // Avoid calling dotenvx when the file doesn't exist, otherwise dotenvx emits - // a noisy [MISSING_ENV_FILE] error to the extension host console. - const envPath = path.join(__dirname, "..", ".env") - if (fs.existsSync(envPath)) { +// The extension-level .env is optional (not shipped in production builds). +// Avoid calling dotenvx when the file doesn't exist, otherwise dotenvx emits +// a noisy [MISSING_ENV_FILE] error to the extension host console. +const envPath = path.join(__dirname, "..", ".env") +if (fs.existsSync(envPath)) { + try { dotenvx.config({ path: envPath }) + } catch (e) { + // Best-effort only: never fail extension activation due to optional env loading. + console.warn("Failed to load environment variables:", e) } -} catch { - // Best-effort only: never fail extension activation due to optional env loading. } import type { CloudUserInfo, AuthState } from "@roo-code/types"