fix(integrations): stop bm subprocesses inheriting Hermes's Python env - #1179
Open
Palo-Alto-AI-Research-Lab wants to merge 1 commit into
Open
Conversation
Hermes runs on its own interpreter and exports PYTHONPATH; bm is a separate
uv-managed tool on Python 3.12+. Every process the plugin spawned inherited
those variables, so the child resolved imports against Hermes's site-packages
and died during interpreter startup:
ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'
That is a compiled-extension mismatch between two Python versions, not a
missing dependency, so it does not respond to reinstalling anything.
Adds _clean_child_env() and applies it at all three spawn sites:
- _BmMcpActor.__init__ -> StdioServerParameters(env=...) for 'bm mcp'
- _install_bm_via_uv -> 'uv tool install basic-memory'
- _ensure_local_project -> 'bm project add'
The last two passed no env= at all, so they inherited implicitly and are easy
to miss when patching only the actor.
PATH, HOME, proxies and credentials are preserved; only the four variables
that name a specific interpreter are dropped.
Tests spawn real subprocesses with a deliberately contaminated PYTHONPATH, as
requested in the issue: a mock cannot reproduce this because the failure
happens in interpreter startup rather than in our code. One test asserts the
contamination reaches an inherited child, so a no-op sanitizer cannot pass.
Fixes basicmachines-co#1093
Signed-off-by: Palo-Alto-AI-Research-Lab <Palo-Alto-AI-Research-Lab@users.noreply.github.com>
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1093.
What
The Hermes plugin spawns three external processes, and every one of them inherited the parent's Python environment. Hermes ships its own interpreter and exports
PYTHONPATH;bmis a separate uv-managed tool on 3.12+. The child therefore resolved imports against Hermes's site-packages and failed during interpreter startup withModuleNotFoundError: No module named 'pydantic_core._pydantic_core'— a compiled-extension mismatch between two Python versions, not a missing dependency, which is why reinstalling doesn't help._clean_child_env()drops the four variables that name a specific interpreter (PYTHONPATH,PYTHONHOME,VIRTUAL_ENV,__PYVENV_LAUNCHER__) and keeps everything else —PATH,HOME, proxies, credentials — because the child still needs them.Applied at all three sites:
_BmMcpActor.__init__→StdioServerParameters(env=…)bm mcpos.environ.copy()_install_bm_via_uvuv tool install basic-memoryenv=— inherited implicitly_ensure_local_projectbm project addenv=— inherited implicitlyThe bottom two are the easy ones to miss: with no
env=argument there is nothing to grep for, anduv tool installis what builds the environment the MCP server later runs in.Testing
tests/test_child_env.py, 10 tests. The four covering the spawn sites were confirmed to fail before the fix and pass after — a sanitizer that did nothing would not go green.The issue asked for an integration test with a contaminated
PYTHONPATHand noted that unit mocks can't reproduce a native-extension ABI failure. That's right, but the failure doesn't need a second interpreter to reproduce deterministically — a directory onPYTHONPATHshadowing a real module does it on one Python:A child spawned with the inherited environment imports the shadow (
ImportError); a child spawned through_clean_child_env()cannot see it at all (ModuleNotFoundError). Both directions are asserted, so the test proves the contamination is real before it proves it is gone. No ABI mismatch to stage in CI and no second interpreter needed.Local runs:
267 passedon Python 3.11, 3.12 and 3.14;just manifest-checkclean;ruff checkandruff format --checkclean.Two scope decisions, in case you'd rather they went the other way
LD_LIBRARY_PATH/DYLD_LIBRARY_PATHare deliberately not stripped. They can break a child the same way, but they also carry legitimate local configuration and were not implicated here. Say the word and I'll add them.__PYVENV_LAUNCHER__is included defensively. The macOS framework launcher exports it to steer a child at a specific interpreter, but I did not reproduce a failure caused by it, unlike the other three. Happy to drop it if you'd rather keep the list to what's proven.I also swept
integrations/openclawfor the same pattern — it has no subprocess spawns, so nothing to fix there.Credit to @tellus1019: the diagnosis in the issue was already correct and complete, including all three scopes. This is that diagnosis implemented, plus a repro that runs in CI.
Disclosure: written by an AI agent acting on behalf of the account holder, as part of Palo Alto AI Research Lab. Everything reported above was run, not assumed; the one untested item is flagged as such.