From 65f608986b595cb4a925426923f34fbea6ff2900 Mon Sep 17 00:00:00 2001 From: Michael Ludlow Date: Mon, 5 Jan 2026 02:05:43 -0500 Subject: [PATCH] fix(python): sanitize environment to prevent PYTHONHOME contamination (#664) * fix(python): sanitize environment to prevent PYTHONHOME contamination Fixes #176 (ACS-33): Ollama embedding download fails with 'Could not find platform independent libraries ' error on Windows. Root cause: When spawning Python subprocesses, the environment was not sanitized. If users had PYTHONHOME set (common with Anaconda, corporate Python installs, or embedded Python), the spawned Python couldn't find its standard library. Changes: - Update getPythonEnv() to build complete env that excludes PYTHONHOME - Pass sanitized env to spawn() in executeOllamaDetector() (2 locations) - Pass sanitized env to spawn() in memory-service.ts executeQuery() - Use sanitized env as base in executeSemanticQuery() This follows the same pattern already used in agent-process.ts for spawning agent Python processes. * fix: address code review feedback - Make PYTHONHOME check case-insensitive for Windows compatibility - Fix type annotation in memory-service.ts (Record) --- .../src/main/ipc-handlers/memory-handlers.ts | 8 ++++- apps/frontend/src/main/memory-service.ts | 7 ++-- apps/frontend/src/main/python-env-manager.ts | 33 ++++++++++++++----- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/apps/frontend/src/main/ipc-handlers/memory-handlers.ts b/apps/frontend/src/main/ipc-handlers/memory-handlers.ts index 5b8c6d05..b89c0c46 100644 --- a/apps/frontend/src/main/ipc-handlers/memory-handlers.ts +++ b/apps/frontend/src/main/ipc-handlers/memory-handlers.ts @@ -25,7 +25,7 @@ import { } from '../memory-service'; import { validateOpenAIApiKey } from '../api-validation-service'; import { parsePythonCommand } from '../python-detector'; -import { getConfiguredPythonPath } from '../python-env-manager'; +import { getConfiguredPythonPath, pythonEnvManager } from '../python-env-manager'; import { openTerminalWithCommand } from './claude-code-handlers'; /** @@ -296,6 +296,9 @@ async function executeOllamaDetector( let resolved = false; const proc = spawn(pythonExe, args, { stdio: ['ignore', 'pipe', 'pipe'], + // Use sanitized Python environment to prevent PYTHONHOME contamination + // Fixes "Could not find platform independent libraries" error on Windows + env: pythonEnvManager.getPythonEnv(), }); let stdout = ''; @@ -769,6 +772,9 @@ export function registerMemoryHandlers(): void { const proc = spawn(pythonExe, args, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 600000, // 10 minute timeout for large models + // Use sanitized Python environment to prevent PYTHONHOME contamination + // Fixes "Could not find platform independent libraries" error on Windows + env: pythonEnvManager.getPythonEnv(), }); let stdout = ''; diff --git a/apps/frontend/src/main/memory-service.ts b/apps/frontend/src/main/memory-service.ts index 0f7a7064..6b52e9ef 100644 --- a/apps/frontend/src/main/memory-service.ts +++ b/apps/frontend/src/main/memory-service.ts @@ -12,7 +12,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { app } from 'electron'; import { findPythonCommand, parsePythonCommand } from './python-detector'; -import { getConfiguredPythonPath } from './python-env-manager'; +import { getConfiguredPythonPath, pythonEnvManager } from './python-env-manager'; import { getMemoriesDir } from './config-paths'; import type { MemoryEpisode } from '../shared/types'; @@ -134,6 +134,8 @@ async function executeQuery( const proc = spawn(pythonExe, fullArgs, { stdio: ['ignore', 'pipe', 'pipe'], timeout, + // Use sanitized Python environment to prevent PYTHONHOME contamination + env: pythonEnvManager.getPythonEnv(), }); let stdout = ''; @@ -193,7 +195,8 @@ async function executeSemanticQuery( const [pythonExe, baseArgs] = parsePythonCommand(pythonCmd); // Build environment with embedder configuration - const env: Record = { ...process.env }; + // Start with sanitized Python env to prevent PYTHONHOME contamination + const env: Record = { ...pythonEnvManager.getPythonEnv() }; // Set the embedder provider env.GRAPHITI_EMBEDDER_PROVIDER = embedderConfig.provider; diff --git a/apps/frontend/src/main/python-env-manager.ts b/apps/frontend/src/main/python-env-manager.ts index 608ba5fd..778c641a 100644 --- a/apps/frontend/src/main/python-env-manager.ts +++ b/apps/frontend/src/main/python-env-manager.ts @@ -619,23 +619,40 @@ if sys.version_info >= (3, 12): /** * Get environment variables that should be set when spawning Python processes. * This ensures Python finds the bundled packages or venv packages. + * + * IMPORTANT: This returns a COMPLETE environment (based on process.env) with + * problematic Python variables removed. This fixes the "Could not find platform + * independent libraries " error on Windows when PYTHONHOME is set. + * + * @see https://github.com/AndyMik90/Auto-Claude/issues/176 */ getPythonEnv(): Record { - const env: Record = { + // Start with process.env but explicitly remove problematic Python variables + // PYTHONHOME causes "Could not find platform independent libraries" when set + // to a different Python installation than the one we're spawning + const baseEnv: Record = {}; + + for (const [key, value] of Object.entries(process.env)) { + // Skip PYTHONHOME - it causes the "platform independent libraries" error + // Use case-insensitive check for Windows compatibility (env vars are case-insensitive on Windows) + // Skip undefined values (TypeScript type guard) + if (key.toUpperCase() !== 'PYTHONHOME' && value !== undefined) { + baseEnv[key] = value; + } + } + + // Apply our Python configuration on top + return { + ...baseEnv, // Don't write bytecode - not needed and avoids permission issues PYTHONDONTWRITEBYTECODE: '1', // Use UTF-8 encoding PYTHONIOENCODING: 'utf-8', // Disable user site-packages to avoid conflicts PYTHONNOUSERSITE: '1', + // Override PYTHONPATH if we have bundled packages + ...(this.sitePackagesPath ? { PYTHONPATH: this.sitePackagesPath } : {}), }; - - // Set PYTHONPATH to our site-packages - if (this.sitePackagesPath) { - env.PYTHONPATH = this.sitePackagesPath; - } - - return env; } /**