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 <prefix>' 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<string, string>)
This commit is contained in:
@@ -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 = '';
|
||||
|
||||
@@ -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<string, string | undefined> = { ...process.env };
|
||||
// Start with sanitized Python env to prevent PYTHONHOME contamination
|
||||
const env: Record<string, string> = { ...pythonEnvManager.getPythonEnv() };
|
||||
|
||||
// Set the embedder provider
|
||||
env.GRAPHITI_EMBEDDER_PROVIDER = embedderConfig.provider;
|
||||
|
||||
@@ -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 <prefix>" error on Windows when PYTHONHOME is set.
|
||||
*
|
||||
* @see https://github.com/AndyMik90/Auto-Claude/issues/176
|
||||
*/
|
||||
getPythonEnv(): Record<string, string> {
|
||||
const env: Record<string, string> = {
|
||||
// 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<string, string> = {};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user