Skip to content

TroubleshootingΒΆ

This guide covers common issues and their solutions when running ContextForge.


macOS: SQLite "disk I/O error"ΒΆ

If the gateway fails on macOS with sqlite3.OperationalError: disk I/O error (works on Linux/Docker), it's usually a filesystem/locking quirk rather than a schema bug.

Quick FixesΒΆ

Avoid iCloud-synced directories:

  • Don't clone/run the repo under ~/Documents or ~/Desktop if iCloud "Desktop & Documents" sync is enabled
  • Use a project folder directly under your home directory:
mkdir -p "$HOME/mcp-context-forge" && cd "$HOME/mcp-context-forge"

Use a safe, local APFS path for SQLite:

Avoid iCloud/Dropbox/OneDrive/Google Drive, network shares, or external exFAT/NAS.

mkdir -p "$HOME/Library/Application Support/mcpgateway"
export DATABASE_URL="sqlite:////Users/$USER/Library/Application Support/mcpgateway/mcp.db"
mkdir -p "$HOME/mcp-context-forge/data"
export DATABASE_URL="sqlite:////Users/$USER/mcp-context-forge/data/mcp.db"

Additional StepsΒΆ

Clean stale SQLite artifacts after any crash:

pkill -f mcpgateway || true && rm -f mcp.db-wal mcp.db-shm mcp.db-journal

Reduce startup concurrency:

GUNICORN_WORKERS=1 make serve  # or use `make dev` which runs single-process

Run the diagnostic helper:

python3 scripts/test_sqlite.py --verbose

Lower pool pressure while debugging:

DB_POOL_SIZE=10 DB_MAX_OVERFLOW=0 DB_POOL_TIMEOUT=60 DB_MAX_RETRIES=10 DB_RETRY_INTERVAL_MS=5000

Disable file-lock leader path (temporary):

export CACHE_TYPE=none

Update SQLite and ensure Python links against it:

brew install sqlite3 && brew link --force sqlite3
brew install python3 && /opt/homebrew/bin/python3 -c 'import sqlite3; print(sqlite3.sqlite_version)'

WSL2: Port Publishing IssuesΒΆ

When using rootless Podman or Docker Desktop on WSL2, you may encounter port publishing issues.

Diagnose the ListenerΒΆ

# Inside your WSL distro
ss -tlnp | grep 4444        # Use ss
netstat -anp | grep 4444    # or netstat

IPv6 Wildcard

Seeing LISTEN rootlessport is normal - the IPv6 wildcard socket (::) also accepts IPv4 traffic when net.ipv6.bindv6only = 0 (default on Linux).

Why localhost Fails on WindowsΒΆ

WSL 2's NAT layer rewrites only the IPv6 side of the dual-stack listener. From Windows, http://127.0.0.1:4444 (or Docker Desktop's "localhost") therefore times out.

Fix for Podman RootlessΒΆ

# Inside the WSL distro
echo "wsl" | sudo tee /etc/containers/podman-machine
systemctl --user restart podman.socket

ss should now show 0.0.0.0:4444 instead of :::4444, and the service becomes reachable from Windows and the LAN.

Fix for Docker Desktop (> 4.19)ΒΆ

Docker Desktop adds a "WSL integration" switch per-distro. Turn it on for your distro, restart Docker Desktop, then restart the container:

docker restart mcpgateway

Gateway Exits ImmediatelyΒΆ

Error: "Failed to read DATABASE_URL" or similar startup failures.

Solution: Copy .env.example to .env and configure required variables:

cp .env.example .env

Then edit DATABASE_URL, JWT_SECRET_KEY, BASIC_AUTH_PASSWORD, etc. Missing or empty required vars cause a fast-fail at startup.

See the Configuration Reference for all available options.


PostgreSQL: ModuleNotFoundError: No module named 'psycopg2'ΒΆ

If the gateway fails at startup with ModuleNotFoundError: No module named 'psycopg2', your DATABASE_URL is using the wrong SQLAlchemy driver dialect.

ContextForge ships with psycopg 3 (psycopg), the modern PostgreSQL adapter. However, SQLAlchemy's default postgresql:// scheme loads the deprecated psycopg2 driver, which is not installed.

FixΒΆ

Change your DATABASE_URL to use the postgresql+psycopg:// scheme:

# Wrong β€” triggers psycopg2 import
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb

# Correct β€” uses the installed psycopg (v3) driver
DATABASE_URL=postgresql+psycopg://user:pass@localhost:5432/mydb

The +psycopg suffix tells SQLAlchemy to use the psycopg 3 dialect instead of the legacy psycopg2 dialect. See the SQLAlchemy Engine Configuration and PostgreSQL dialect documentation for details on database URL schemes.

Tip

If you also see postgres:// (without the ql), note that SQLAlchemy requires the full postgresql prefix. Some providers (e.g., Heroku) supply URLs starting with postgres:// β€” these need to be rewritten to postgresql+psycopg://.


Team Member Limit ExceededΒΆ

If you see an error such as "Team has reached maximum member limit of 100" when adding members or sending invitations, the team has hit its membership cap.

Each team has a max_members value. When max_members is null (the default for new teams), the global MAX_MEMBERS_PER_TEAM setting is used at check time (default: 100). The limit applies to active members plus pending invitations.

FixΒΆ

Increase the global default by setting MAX_MEMBERS_PER_TEAM in your environment. This takes effect immediately for all teams that do not have an explicit per-team override:

# .env
MAX_MEMBERS_PER_TEAM=500

Alternatively, set an explicit per-team limit via the Admin API:

curl -X PUT http://localhost:4444/teams/{team_id} \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"max_members": 500}'

To revert a team to the global default, clear its per-team override:

curl -X PUT http://localhost:4444/teams/{team_id} \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"max_members": null}'

See the Configuration Reference for all team-related settings.


Common IssuesΒΆ

Issue Solution
ModuleNotFoundError: mcpgateway Run make install-dev or pip install -e .
Port already in use Check for existing processes: lsof -i :4444
Authentication failures Verify JWT_SECRET_KEY matches token generation
Database locked Reduce workers: GUNICORN_WORKERS=1
SSL certificate errors Generate certs: make certs

Getting HelpΒΆ