The Graphiti MCP server running inside E2B sandboxes returns a 401 Unauthorized error when executing tools that require OpenAI API access (such as mcp_graphiti_search_nodes and mcp_graphiti_search_memory_facts). The error message states:
Error code: 401 - {'error': {'message': "You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth..."}}
The core issue: The OPENAI_API_KEY environment variable is not reaching the Graphiti process inside the E2B sandbox, despite being configured in multiple places and passed through various mechanisms.
The API key must flow through this chain:
backend/.env → Settings (conf.py) → SandboxConfig → E2B Sandbox Creation →
→ sandbox.commands.run() → start-services.sh → tmux session → Graphiti process
Confirmed working:
- ✅
backend/.envcontainsOPENAI_API_KEY - ✅
settings.OPENAI_API_KEYloads correctly - ✅
SandboxConfig.openai_api_keyis populated - ✅ Backend logs show "Passing OPENAI_API_KEY to sandbox"
Confirmed broken:
- ❌ Inside the sandbox,
/tmp/sandbox_envfile is created but contains NO API key - ❌ The Graphiti process environment shows
OPENAI_API_KEY=(empty) - ❌ Graphiti fails with 401 when calling OpenAI embeddings
OPENAI_API_KEY='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'
OPENAI_MODEL="gpt-5.2"
OPENAI_BASE_URL='https://api.openai.com/v1'OPENAI_API_KEY: str = ''# API keys to pass to sandbox (for services like Graphiti that need LLM access)
data.setdefault("openai_api_key", getattr(settings, "OPENAI_API_KEY", None) or os.getenv("OPENAI_API_KEY"))# Build environment variables to pass to sandbox
sandbox_envs = {}
if config.openai_api_key:
sandbox_envs["OPENAI_API_KEY"] = config.openai_api_key
logger.info(f"Passing OPENAI_API_KEY to sandbox (key starts with: {config.openai_api_key[:20]}...)")
else:
logger.warning("No OPENAI_API_KEY found in config - Graphiti will fail!")
sandbox = await AsyncSandbox.create(
sandbox_template_id if sandbox_template_id else config.e2b_template_id,
api_key=config.e2b_api_key,
metadata=metadata,
envs=sandbox_envs if sandbox_envs else None, # Passed to E2B
)
# Start services - THIS IS WHERE THE ISSUE LIKELY IS
try:
logger.info(f"Starting services in sandbox {sandbox_id}")
# Build environment variable exports for the command
env_exports = ""
if sandbox_envs:
# Create exports inside the bash -c string
env_parts = [f'export {k}="{v}"' for k, v in sandbox_envs.items()]
env_exports = "; ".join(env_parts) + "; "
# Use bash -c to ensure env vars are in the same shell as the script
cmd = f"bash -c '{env_exports}/app/start-services.sh' &"
logger.info(f"Running start command with {len(sandbox_envs)} env vars")
await sandbox.commands.run(cmd, timeout=30)
except Exception as e:
logger.warning(f"Service startup command returned: {e}")#!/bin/bash
set -e
# If running as root, use gosu to re-execute as pn user
if [ "$(id -u)" = "0" ]; then
echo "Running as root, switching to pn user with gosu..."
exec gosu pn env OPENAI_API_KEY="${OPENAI_API_KEY:-}" bash "$0" "$@"
fi
# Set up environment
export HOME=/home/pn
export PATH="/home/pn/.bun/bin:/app/agents_backend/.venv/bin:$PATH"
export npm_config_cache=/home/pn/.npm
# Write API keys to a file so tmux sessions can source them
echo "# Sandbox environment variables" > /tmp/sandbox_env
if [ -n "$OPENAI_API_KEY" ]; then
echo "export OPENAI_API_KEY=\"$OPENAI_API_KEY\"" >> /tmp/sandbox_env
echo "OPENAI_API_KEY found and written to /tmp/sandbox_env"
else
echo "WARNING: OPENAI_API_KEY not set - Graphiti will fail!"
fi
chmod 644 /tmp/sandbox_envif [ -d /app/graphiti-mcp ]; then
echo "Starting Graphiti MCP Server on port $GRAPHITI_MCP_PORT..."
# Source env vars from file (tmux doesn't inherit parent env vars)
tmux new-session -d -s graphiti-mcp-system-never-kill -c /app/graphiti-mcp \
"source /tmp/sandbox_env 2>/dev/null; FALKORDB_URI=redis://localhost:$GRAPHITI_FALKORDB_PORT MCP_SERVER_HOST=0.0.0.0 MCP_SERVER_PORT=$GRAPHITI_MCP_PORT UV_PYTHON_DOWNLOADS=never uv run --no-sync main.py --transport http --port $GRAPHITI_MCP_PORT 2>&1 | tee /tmp/graphiti-mcp.log"
fivolumes:
- ./deploy/backend/docker-compose/.env.server:/agents_backend/backend/.envOPENAI_API_KEY='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'# Check sandbox creation logs (look for OPENAI_API_KEY passing)
docker exec agents_backend_server sh -c "grep -E 'OPENAI|Passing|env vars|sandbox' /var/log/agents_backend/agents_backend_server.log | tail -30"
# Check for errors during startup
docker exec agents_backend_server sh -c "grep -i 'error\|exception\|traceback' /var/log/agents_backend/agents_backend_server.log | tail -20"
# Check the actual command being run
docker exec agents_backend_server sh -c "grep 'Running start command' /var/log/agents_backend/agents_backend_server.log | tail -10"# Verify the e2b.py has the latest fixes
docker exec agents_backend_server sh -c "grep -A20 'Build environment variable' /agents_backend/backend/src/sandbox/sandbox_server/sandboxes/e2b.py"
# Check if env_exports logic exists
docker exec agents_backend_server sh -c "grep -c 'env_exports' /agents_backend/backend/src/sandbox/sandbox_server/sandboxes/e2b.py"# Check if .env is mounted correctly
docker exec agents_backend_server sh -c "grep OPENAI_API_KEY /agents_backend/backend/.env | head -1"
# Check settings loading
docker exec agents_backend_server python -c "from backend.core.conf import settings; print(f'OPENAI_API_KEY set: {bool(settings.OPENAI_API_KEY)}')"Access the sandbox VS Code terminal at https://9000-<sandbox-id>.e2b.app and run:
# Check if OPENAI_API_KEY is in current environment
echo $OPENAI_API_KEY
env | grep OPENAI
# Check the sandbox_env file that should contain the key
cat /tmp/sandbox_env
# Check if the file has the key
grep OPENAI_API_KEY /tmp/sandbox_env# Find the Graphiti process and check its environment
ps aux | grep graphiti
# Get the PID and check its environment
cat /proc/<PID>/environ | tr '\0' '\n' | grep OPENAI# Verify the script has the env writing logic
cat /app/start-services.sh | grep -A10 "sandbox_env"
# Check the gosu line
cat /app/start-services.sh | grep gosu# Check Graphiti MCP server logs for errors
cat /tmp/graphiti-mcp.log | tail -50
# Look for API key related errors
grep -i "api\|key\|401\|auth" /tmp/graphiti-mcp.log-
Is the E2B
envsparameter working? - When sandbox is created withenvs={"OPENAI_API_KEY": "..."}, does the sandbox shell have this variable? -
Is
sandbox.commands.run()preserving env vars? - When running a command with env vars, does the subshell inherit them? -
Does the bash -c wrapper work with background processes? - Does
bash -c 'export X=Y; cmd' &properly export to the command? -
Is
gosupreserving the environment? - When switching from root to pn user, doesgosu pn env OPENAI_API_KEY="$OPENAI_API_KEY" bash...work? -
Is tmux inheriting parent environment? - When
tmux new-sessionruns, does the new session have access to exported variables? -
Is the E2B template up to date? - Does the template contain the latest
start-services.shwith the /tmp/sandbox_env file logic?
The Graphiti process was started with an empty API key:
ps aux output shows:
bash -c OPENAI_API_KEY= FALKORDB_URI=redis://localhost:6380 ... uv run main.py
The /tmp/sandbox_env file contains only:
# Sandbox environment variables
(No OPENAI_API_KEY line)
| File | Path | Purpose |
|---|---|---|
| Backend .env | backend/.env |
Source of OPENAI_API_KEY |
| Docker .env.server | deploy/backend/docker-compose/.env.server |
Docker mounted env |
| Settings | backend/core/conf.py |
Pydantic settings loader |
| Sandbox Config | backend/src/sandbox/sandbox_server/config.py |
Sandbox config with API key |
| E2B Sandbox | backend/src/sandbox/sandbox_server/sandboxes/e2b.py |
Creates sandbox, passes env vars |
| Start Script | backend/docker/sandbox/start-services.sh |
Starts services in sandbox (in E2B template) |
| E2B Dockerfile | backend/e2b.Dockerfile |
Builds the E2B template |
| Docker Compose | docker-compose.yml |
Mounts env files |