This guide provides symptom-based troubleshooting for Fortemi. Run the Quick Diagnostics first to identify the problem area, then jump to the relevant section.
Run these commands to identify the problem area:
# 1. Check container status
docker compose -f docker-compose.bundle.yml ps
# 2. Check API health
curl http://localhost:3000/health
# 3. Check MCP health
curl http://localhost:3001/health
# 4. Check logs for errors
docker compose -f docker-compose.bundle.yml logs --tail=50
# 5. Check database connectivity
docker exec Fortémi-matric-1 psql -U matric -d matric -c "SELECT 1"
# 6. Check Ollama connectivity (from host)
curl http://localhost:11434/api/tags
# 7. Check environment variables
docker exec Fortémi-matric-1 printenv | grep -E 'DATABASE|OLLAMA|MCP|ISSUER'Symptom: docker compose up fails or container exits immediately.
Diagnosis:
# Check container status
docker compose -f docker-compose.bundle.yml ps
# Check for port conflicts
netstat -tulpn | grep -E ':(3000|3001|5432)'
# Check Docker daemon
docker info
# Check for image
docker images | grep FortémiFix:
Port conflict:
# Identify process using port
lsof -i :3000
# Kill process or change Fortémi port in docker-compose.bundle.yml
# Edit ports section: "3100:3000" instead of "3000:3000"Missing image:
# Build the image
docker compose -f docker-compose.bundle.yml build
# Or pull if available
docker compose -f docker-compose.bundle.yml pullDocker not running:
# Start Docker daemon (systemd)
sudo systemctl start docker
# Or on macOS/Windows, start Docker DesktopSymptom: API logs show "connection refused" or "could not connect to database".
Diagnosis:
# Check DATABASE_URL environment variable
docker exec Fortémi-matric-1 printenv DATABASE_URL
# Check if PostgreSQL is running
docker exec Fortémi-matric-1 pg_isready -U matric -d matric
# Test connection manually
docker exec Fortémi-matric-1 psql -U matric -d matric -c "SELECT version()"
# Check logs for PostgreSQL errors
docker compose -f docker-compose.bundle.yml logs | grep -i postgresFix:
Wrong DATABASE_URL:
# Correct format should be:
# postgres://matric:matric@localhost/matric
# Stop container
docker compose -f docker-compose.bundle.yml down
# Verify DATABASE_URL in docker-compose.bundle.yml or .env
# Restart
docker compose -f docker-compose.bundle.yml up -dPostgreSQL not ready (transient on first start):
# Wait 10-15 seconds for PostgreSQL initialization
# Then restart API container
docker compose -f docker-compose.bundle.yml restart matricpgvector extension missing:
# Connect to database
docker exec -it Fortémi-matric-1 psql -U matric -d matric
# Check for extension
SELECT * FROM pg_extension WHERE extname = 'vector';
# If missing, install (should be automatic on first run)
CREATE EXTENSION IF NOT EXISTS vector;Symptom: Container logs show migration failures during startup.
Diagnosis:
# Check migration logs
docker compose -f docker-compose.bundle.yml logs | grep -i migration
# Check current migration version
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT version FROM _sqlx_migrations ORDER BY version DESC LIMIT 1"
# List all migrations
ls migrations/Fix:
Transient errors (network, timing):
# Simply restart the container
docker compose -f docker-compose.bundle.yml restart matric
# Migrations run automatically on startupCREATE INDEX CONCURRENTLY in transaction:
# This is a known PostgreSQL limitation
# The migration must be rewritten to avoid using CONCURRENTLY in tests
# Or tests must use manual pool setup instead of #[sqlx::test]
# No operator fix needed - report to developersEnum type conflicts:
# Check for error like "type already exists"
docker compose -f docker-compose.bundle.yml logs | grep "already exists"
# This usually means partial migration
# Connect to database and check
docker exec -it Fortémi-matric-1 psql -U matric -d matric
# Check custom types
\dT+
# If migration is stuck, may need to roll back manually
# Contact developers with specific error messageOld Docker image:
# Rebuild from latest code
docker compose -f docker-compose.bundle.yml build --no-cache
docker compose -f docker-compose.bundle.yml up -dSymptom: "port is already allocated" error during container start.
Diagnosis:
# Check what's using the ports
netstat -tulpn | grep -E ':(3000|3001|5432)'
# Or on macOS
lsof -i :3000
lsof -i :3001
lsof -i :5432
# Check if old Fortémi container still running
docker ps -a | grep matricFix:
Port 3000 (API):
# Option 1: Kill the conflicting process
sudo kill -9 <PID>
# Option 2: Change Fortémi port in docker-compose.bundle.yml
# Edit the ports section:
ports:
- "3100:3000" # API now on 3100Port 3001 (MCP):
# Change MCP port in docker-compose.bundle.yml
ports:
- "3101:3001" # MCP now on 3101
# Update nginx proxy config if using reverse proxyPort 5432 (PostgreSQL):
# Check if another PostgreSQL instance is running
ps aux | grep postgres
# The bundle uses internal PostgreSQL, so port 5432 should not be exposed
# If docker-compose.bundle.yml exposes 5432, remove that lineSymptom: All searches return empty results or "no results found".
Diagnosis:
# 1. Check if notes exist
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM notes"
# 2. Check if FTS is populated
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM notes WHERE fts IS NOT NULL"
# 3. Check if embeddings exist (for semantic search)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM note_embeddings"
# 4. Test simple query via API
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"test","mode":"fts"}'Fix:
No notes created:
# Create a test note via API
curl -X POST http://localhost:3000/notes \
-H "Content-Type: application/json" \
-d '{"title":"Test Note","body":"This is a test note for search"}'FTS index not populated:
# FTS should populate automatically on note creation
# Check logs for errors
docker compose -f docker-compose.bundle.yml logs | grep -i fts
# Manually trigger FTS update (requires database access)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"UPDATE notes SET fts = to_tsvector('english', title || ' ' || body)"Embeddings not generated (semantic search):
# Check job queue status
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT status, COUNT(*) FROM job_queue WHERE job_type = 'embedding' GROUP BY status"
# If jobs are stuck, check Ollama connectivity (see "Embedding and AI Issues")
# Or reset failed jobs
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue SET status = 'pending' WHERE status = 'failed' AND job_type = 'embedding'"Wrong search mode:
# Try different modes:
# - "fts" for exact keyword matching
# - "semantic" for conceptual similarity
# - "hybrid" for combined approach
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"your search term","mode":"hybrid"}'Symptom: Search returns unrelated notes or poor ranking.
Diagnosis:
# Check which search mode is being used
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"your term","mode":"hybrid","explain":true}'
# Check if multilingual features are enabled
docker exec Fortémi-matric-1 printenv | grep FTS_
# Test with specific mode
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"your term","mode":"fts"}'Fix:
Wrong search mode for query type:
For exact keyword matching (technical terms, names, IDs):
# Use FTS mode
{"query":"REQ-001","mode":"fts"}For conceptual/semantic queries (questions, topics):
# Use semantic mode
{"query":"how to configure authentication","mode":"semantic"}For general queries (most cases):
# Use hybrid mode with adaptive weighting
{"query":"authentication setup","mode":"hybrid"}Multilingual search not working:
# Enable multilingual feature flags
# Edit docker-compose.bundle.yml or .env:
environment:
- FTS_SCRIPT_DETECTION=true
- FTS_MULTILINGUAL_CONFIGS=true
- FTS_TRIGRAM_FALLBACK=true
- FTS_BIGRAM_CJK=true
# Restart container
docker compose -f docker-compose.bundle.yml restart matricWrong language configuration:
# Provide language hint in search request
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"你好","mode":"hybrid","language":"zh"}'Symptom: Search takes more than 1-2 seconds to return results.
Diagnosis:
# 1. Check database query performance
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"EXPLAIN ANALYZE SELECT id FROM notes WHERE fts @@ websearch_to_tsquery('english', 'test')"
# 2. Check HNSW index status
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'note_embeddings'"
# 3. Check result set size
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"test","mode":"hybrid","limit":1000}'
# 4. Monitor query time in logs
docker compose -f docker-compose.bundle.yml logs -f | grep -i searchFix:
HNSW index missing or misconfigured:
# Check if index exists
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT indexname FROM pg_indexes WHERE indexname LIKE '%hnsw%'"
# If missing, create HNSW index (adjust dimensions to match your model)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"CREATE INDEX IF NOT EXISTS note_embeddings_hnsw_idx ON note_embeddings
USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64)"ef_search too high:
# Reduce ef_search for faster queries (trades recall for speed)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SET hnsw.ef_search = 40" # Default is 100
# Or set globally in PostgreSQL config
# Add to postgresql.conf: hnsw.ef_search = 40Large result sets:
# Limit results in search request
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"test","mode":"hybrid","limit":20}'
# Enable pagination
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"test","mode":"hybrid","limit":20,"offset":0}'Strict filter optimization needed:
# Use tag filtering for better performance on large datasets
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"test","mode":"hybrid","tags":["project:alpha"]}'Symptom: Non-English searches return no results or poor results.
Diagnosis:
# Check feature flags
docker exec Fortémi-matric-1 printenv | grep FTS_
# Check available text search configurations
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT cfgname FROM pg_ts_config"
# Test with specific language
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{"query":"Hallo Welt","mode":"fts","language":"de"}'Fix:
Feature flags not enabled:
# Edit docker-compose.bundle.yml or .env
environment:
- FTS_SCRIPT_DETECTION=true # Auto-detect query language
- FTS_MULTILINGUAL_CONFIGS=true # Use language-specific stemming
- FTS_TRIGRAM_FALLBACK=true # Enable emoji/symbol search
- FTS_BIGRAM_CJK=true # Optimize CJK search
# Restart container
docker compose -f docker-compose.bundle.yml restart matricLanguage hint not provided:
# Provide explicit language hint
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{
"query":"你好世界",
"mode":"hybrid",
"language":"zh"
}'CJK-specific setup:
# Ensure pg_bigm or pg_trgm is installed
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT * FROM pg_extension WHERE extname IN ('pg_bigm', 'pg_trgm')"
# If missing, install pg_trgm (pg_bigm optional)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"CREATE EXTENSION IF NOT EXISTS pg_trgm"Symptom: Job logs show "connection refused" to Ollama, or embedding jobs fail immediately.
Diagnosis:
# 1. Check if Ollama is running on host
curl http://localhost:11434/api/tags
# 2. Check OLLAMA_URL from container
docker exec Fortémi-matric-1 printenv OLLAMA_URL
# 3. Test connectivity from container
docker exec Fortémi-matric-1 curl http://host.docker.internal:11434/api/tags
# 4. Check job failures
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT id, error FROM job_queue WHERE status = 'failed' AND job_type = 'embedding' LIMIT 5"Fix:
Ollama not installed:
# Install Ollama on host
curl -fsSL https://ollama.com/install.sh | sh
# Start Ollama service
ollama serveOllama not running:
# Start Ollama
ollama serve
# Or as systemd service
sudo systemctl start ollamaDocker cannot reach host (missing extra_hosts):
# Edit docker-compose.bundle.yml
# Add under the matric service:
extra_hosts:
- "host.docker.internal:host-gateway"
# Update OLLAMA_URL to use host.docker.internal
environment:
- OLLAMA_URL=http://host.docker.internal:11434
# Restart container
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -dWrong OLLAMA_URL:
# Check current URL
docker exec Fortémi-matric-1 printenv OLLAMA_URL
# Should be http://host.docker.internal:11434 from Docker
# Or http://localhost:11434 if running API outside Docker
# Update in docker-compose.bundle.yml or .env
# Restart container
docker compose -f docker-compose.bundle.yml restart matricSymptom: Job queue shows many failed embedding jobs.
Diagnosis:
# Check job status
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT status, COUNT(*) FROM job_queue WHERE job_type = 'embedding' GROUP BY status"
# Check recent failures
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT id, error FROM job_queue
WHERE status = 'failed' AND job_type = 'embedding'
ORDER BY updated_at DESC LIMIT 5"
# Check logs
docker compose -f docker-compose.bundle.yml logs | grep -i "embed"
# Check Ollama model availability
curl http://localhost:11434/api/tags | grep -i embeddingFix:
Model not pulled:
# Pull required embedding model
ollama pull nomic-embed-text
# Or your configured model
ollama pull <model-name>
# Verify model is available
ollama listOOM (Out of Memory) errors:
# Use smaller or quantized model
# Edit docker-compose.bundle.yml or .env:
environment:
- EMBEDDING_MODEL=nomic-embed-text:q4_0 # Quantized version
# Or reduce parallel requests
environment:
- OLLAMA_NUM_PARALLEL=1
# Restart and reset failed jobs
docker compose -f docker-compose.bundle.yml restart matric
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue SET status = 'pending' WHERE status = 'failed' AND job_type = 'embedding'"Connection timeout:
# Increase timeout in configuration
# Edit docker-compose.bundle.yml or .env:
environment:
- OLLAMA_TIMEOUT_SECONDS=300
# Restart container
docker compose -f docker-compose.bundle.yml restart matricOld URL cached in failed jobs:
# Reset all failed embedding jobs
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue
SET status = 'pending', error = NULL, attempts = 0
WHERE status = 'failed' AND job_type = 'embedding'"
# Worker will retry with current configurationSymptom: Semantic or hybrid search returns empty results, but FTS works.
Diagnosis:
# Check if embeddings exist
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM note_embeddings"
# Check embedding dimension
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT note_id, vector_dims(embedding) FROM note_embeddings LIMIT 1"
# Check for pending embedding jobs
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM job_queue WHERE job_type = 'embedding' AND status = 'pending'"
# Test embedding generation manually
curl -X POST http://localhost:11434/api/embeddings \
-d '{"model":"nomic-embed-text","prompt":"test query"}'Fix:
Embeddings not generated:
# Trigger embedding for all notes
curl -X POST http://localhost:3000/admin/reindex
# Or wait for background jobs to complete
# Check progress
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT status, COUNT(*) FROM job_queue WHERE job_type = 'embedding' GROUP BY status"Wrong embedding dimension:
# This indicates model mismatch
# Check current model configuration
docker exec Fortémi-matric-1 printenv EMBEDDING_MODEL
# Verify model dimension
curl http://localhost:11434/api/show -d '{"name":"nomic-embed-text"}' | grep embedding
# If dimension changed, must re-embed all notes
# WARNING: This deletes existing embeddings
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"TRUNCATE note_embeddings"
# Trigger re-embedding
curl -X POST http://localhost:3000/admin/reindexInsufficient notes for semantic search:
# Semantic search requires multiple embedded notes
# Create more test notes
for i in {1..10}; do
curl -X POST http://localhost:3000/notes \
-H "Content-Type: application/json" \
-d "{\"title\":\"Test Note $i\",\"body\":\"Content about topic $i\"}"
done
# Wait for embedding jobs to completeSymptom: Related notes are not automatically linked.
Diagnosis:
# Check if embeddings exist
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM note_embeddings"
# Check linking job status
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT status, COUNT(*) FROM job_queue WHERE job_type = 'link' GROUP BY status"
# Check existing links
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM note_links WHERE link_type = 'semantic'"
# Check similarity threshold
docker exec Fortémi-matric-1 printenv SEMANTIC_LINK_THRESHOLDFix:
Embedding jobs not complete:
# Wait for embedding jobs to finish first
# Check progress
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT status, COUNT(*) FROM job_queue WHERE job_type = 'embedding' GROUP BY status"
# Once embeddings are complete, linking jobs will run automaticallySimilarity threshold too high:
# Default threshold is 0.7 (70% similarity)
# Lower threshold to find more links (0.6 = 60%)
# Edit docker-compose.bundle.yml or .env:
environment:
- SEMANTIC_LINK_THRESHOLD=0.6
# Restart container
docker compose -f docker-compose.bundle.yml restart matric
# Trigger re-linking
curl -X POST http://localhost:3000/admin/relinkInsufficient notes:
# Auto-linking requires at least 2 similar notes
# Create more related notes
curl -X POST http://localhost:3000/notes \
-H "Content-Type: application/json" \
-d '{"title":"Topic A","body":"Detailed content about topic A"}'
curl -X POST http://localhost:3000/notes \
-H "Content-Type: application/json" \
-d '{"title":"Related to Topic A","body":"More content about topic A"}'
# Wait for embedding and linking jobs to completeSymptom: Container crashes or Ollama shows out-of-memory errors during embedding.
Diagnosis:
# Check container memory limit
docker stats Fortémi-matric-1
# Check Ollama logs
journalctl -u ollama -n 50
# Check current embedding model
docker exec Fortémi-matric-1 printenv EMBEDDING_MODEL
# Check model size
ollama listFix:
Model too large for VRAM:
# Use smaller embedding model
# Edit docker-compose.bundle.yml or .env:
environment:
- EMBEDDING_MODEL=all-minilm:l6-v2 # Small model (384 dims)
# Or use quantized version
environment:
- EMBEDDING_MODEL=nomic-embed-text:q4_0
# Restart container
docker compose -f docker-compose.bundle.yml restart matric
# Re-embed notes (WARNING: deletes existing embeddings)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"TRUNCATE note_embeddings"
curl -X POST http://localhost:3000/admin/reindexReduce batch size:
# Reduce parallel embedding requests
# Edit docker-compose.bundle.yml or .env:
environment:
- OLLAMA_NUM_PARALLEL=1
- WORKER_THREADS=2
# Restart container
docker compose -f docker-compose.bundle.yml restart matricIncrease container memory:
# Edit docker-compose.bundle.yml
# Add under matric service:
deploy:
resources:
limits:
memory: 4G
# Restart container
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -dSymptom: PDF extraction jobs fail with PostgreSQL error 22P05: unsupported Unicode escape sequence.
Cause: Old PDFs (Acrobat 3.0/4.0 era) embed null bytes (\u0000) in metadata fields like Creator. PostgreSQL rejects null bytes in text/JSON columns.
Fix: Upgrade to v2026.2.10 or later. The PdfTextAdapter now strips null bytes from both metadata and extracted text before database storage. See ADR-085.
If you're on an older version, reset the failed jobs after upgrading:
docker exec fortemi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue SET status = 'pending', error_message = NULL
WHERE status = 'failed'
AND error_message LIKE '%unsupported Unicode escape%'"Symptom: MCP authentication fails with "Protected resource URL mismatch" error.
Diagnosis:
# Check ISSUER_URL environment variable
docker exec Fortémi-matric-1 printenv ISSUER_URL
# Verify OAuth well-known endpoint
curl http://localhost:3000/.well-known/oauth-authorization-server
# Check if ISSUER_URL matches the "issuer" field in responseFix:
# Set ISSUER_URL in .env file
# Must match your deployment domain
echo "ISSUER_URL=http://localhost:3000" >> .env
# Or for local testing
echo "ISSUER_URL=http://localhost:3000" >> .env
# Restart container
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -d
# Verify setting
docker exec Fortémi-matric-1 printenv ISSUER_URLSymptom: MCP server returns 401 Unauthorized even with valid OAuth token.
Diagnosis:
# Check MCP_CLIENT_ID and MCP_CLIENT_SECRET
docker exec Fortémi-matric-1 printenv | grep MCP_CLIENT
# Test token introspection manually
curl -X POST http://localhost:3000/oauth/introspect \
-u "$MCP_CLIENT_ID:$MCP_CLIENT_SECRET" \
-d "token=<your-token>"
# Check MCP server logs
docker compose -f docker-compose.bundle.yml logs | grep -i mcpFix:
MCP credentials not configured:
# Register OAuth client first
curl -X POST http://localhost:3000/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "MCP Server",
"grant_types": ["client_credentials"],
"scope": "mcp read"
}'
# Save the returned client_id and client_secret
# Add to .env file
echo "MCP_CLIENT_ID=mm_xxxxx" >> .env
echo "MCP_CLIENT_SECRET=xxxxx" >> .env
# Restart container
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -dWrong scope:
# Ensure client has "mcp" and "read" scopes
# Re-register with correct scopes (see above)Symptom: Claude Code shows "Authentication successful" but then reconnection fails.
Diagnosis:
# Check for stale credentials in Claude credentials file
cat ~/.claude/.credentials.json | grep Fortémi
# Check MCP server logs for auth errors
docker compose -f docker-compose.bundle.yml logs | grep -i "auth"
# Test MCP endpoint directly
curl http://localhost:3001/healthFix:
# Remove stale OAuth credentials from Claude cache
# Edit ~/.claude/.credentials.json
# Remove or update the "Fortémi" entry
# Or delete entire credentials file (will require re-auth)
rm ~/.claude/.credentials.json
# Restart Claude Code
# Re-authenticate when promptedSymptom: MCP endpoint returns connection refused or times out.
Diagnosis:
# Check if container is running
docker compose -f docker-compose.bundle.yml ps
# Check MCP server logs
docker compose -f docker-compose.bundle.yml logs | grep -i mcp
# Test MCP port directly
curl http://localhost:3001/health
# Check if Node.js process is running
docker exec Fortémi-matric-1 ps aux | grep nodeFix:
Container crashed:
# Restart container
docker compose -f docker-compose.bundle.yml restart matric
# Check logs for crash reason
docker compose -f docker-compose.bundle.yml logs --tail=100MCP server process died:
# Check if supervisor is running
docker exec Fortémi-matric-1 ps aux | grep supervisor
# If not, restart container
docker compose -f docker-compose.bundle.yml restart matric
# Check logs
docker compose -f docker-compose.bundle.yml logs -fPort not exposed:
# Verify docker-compose.bundle.yml exposes port 3001
# Should have:
ports:
- "3001:3001"
# If missing, add and restart
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -dSymptom: Accessing MCP through Nginx reverse proxy returns 502 Bad Gateway.
Diagnosis:
# Test direct container access (without proxy)
curl http://localhost:3001/health
# Check nginx error logs
sudo tail -f /var/log/nginx/error.log
# Check nginx configuration
sudo nginx -t
# Test if nginx can reach container
docker inspect Fortémi-matric-1 | grep IPAddress
curl http://<container-ip>:3001/healthFix:
Nginx cannot reach container:
# Ensure nginx is configured to proxy to correct host
# nginx.conf should have:
upstream mcp_backend {
server localhost:3001;
}
server {
listen 443 ssl;
server_name your-domain.com;
location /mcp {
proxy_pass http://mcp_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
proxy_connect_timeout 600s;
}
}
# Reload nginx
sudo nginx -s reloadWebSocket upgrade headers missing:
# Add WebSocket support to nginx config (see above)
# The critical headers are:
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# Reload nginx
sudo nginx -s reloadSymptom: WebSocket connections to /api/v1/ws disconnect frequently.
Diagnosis:
# Test WebSocket directly (bypass nginx)
wscat -c ws://localhost:3000/api/v1/ws
# Check nginx configuration
nginx -tFix:
Missing nginx WebSocket headers:
location /api/v1/ws {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400; # 24h timeout for long-lived connections
}Proxy timeout too short:
# Increase proxy_read_timeout in nginx config
# Default 60s is too short for WebSocket connections
# Use 86400 (24 hours) for persistent connectionsSymptom: Connected to /api/v1/events but no events arrive.
Diagnosis:
# Test SSE directly
curl -N http://localhost:3000/api/v1/events
# Check if events are being emitted (should see keepalive every 15s)
# If no keepalive, the connection may be buffered by a proxyFix:
Nginx buffering SSE responses:
location /api/v1/events {
proxy_pass http://localhost:3000;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400;
}No active jobs or notes being processed:
# QueueStatus events only emit when subscribers are connected
# Create a note to trigger events
curl -X POST http://localhost:3000/api/v1/notes \
-H "Content-Type: application/json" \
-d '{"content": "test note"}'Symptom: Webhooks created but target URL not receiving events.
Diagnosis:
# Check webhook configuration
curl http://localhost:3000/api/v1/webhooks
# Check delivery logs for a webhook
curl http://localhost:3000/api/v1/webhooks/<webhook-id>/deliveries?limit=10
# Test webhook delivery manually
curl -X POST http://localhost:3000/api/v1/webhooks/<webhook-id>/testFix:
Target URL unreachable:
# Verify the webhook URL is accessible from the container
docker exec Fortémi-matric-1 curl -v https://your-webhook-url.comWebhook not subscribed to correct events:
# Update webhook to subscribe to specific events
curl -X PATCH http://localhost:3000/api/v1/webhooks/<webhook-id> \
-H "Content-Type: application/json" \
-d '{"events": ["NoteUpdated", "JobCompleted", "JobFailed"]}'HMAC signature validation failing:
# Verify the secret matches what the receiver expects
# Signature is HMAC-SHA256 of the JSON payload
# Header: X-Fortemi-Signature: sha256=<hex-digest>Symptom: Container uses excessive memory (multiple GB) or gets OOM-killed.
Diagnosis:
# Check container memory usage
docker stats Fortémi-matric-1
# Check process memory inside container
docker exec Fortémi-matric-1 ps aux --sort=-%mem | head -10
# Check PostgreSQL memory settings
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SHOW shared_buffers; SHOW work_mem; SHOW maintenance_work_mem;"
# Check Ollama model size
ollama listFix:
Embedding model too large:
# See "OOM Errors During Embedding" section above
# Use smaller or quantized modelToo many parallel requests:
# Reduce worker threads and parallel Ollama requests
# Edit docker-compose.bundle.yml or .env:
environment:
- WORKER_THREADS=2
- OLLAMA_NUM_PARALLEL=1
# Restart container
docker compose -f docker-compose.bundle.yml restart matricPostgreSQL work_mem too high:
# Reduce PostgreSQL memory allocation
# Edit postgresql.conf or set in docker-compose.bundle.yml:
command: >
postgres
-c shared_buffers=256MB
-c work_mem=16MB
-c maintenance_work_mem=64MB
# Restart container
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -dSet container memory limit:
# Edit docker-compose.bundle.yml
# Add under matric service:
deploy:
resources:
limits:
memory: 2G
# Restart container
docker compose -f docker-compose.bundle.yml down
docker compose -f docker-compose.bundle.yml up -dSymptom: API requests take several seconds to return.
Diagnosis:
# Enable slow query logging
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"ALTER DATABASE matric SET log_min_duration_statement = 1000"
# Check slow queries in logs
docker compose -f docker-compose.bundle.yml logs | grep "duration:"
# Analyze specific query
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"EXPLAIN ANALYZE SELECT * FROM notes WHERE fts @@ websearch_to_tsquery('test')"
# Check index usage
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan"Fix:
Missing indexes:
# Check what indexes exist
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT tablename, indexname FROM pg_indexes WHERE schemaname = 'public'"
# Common indexes should include:
# - notes_fts_idx (GIN index on fts column)
# - note_embeddings_hnsw_idx (HNSW index on embedding column)
# - notes_created_at_idx (for sorting by date)
# If missing, migrations should create them
# Rebuild container to run migrations
docker compose -f docker-compose.bundle.yml build
docker compose -f docker-compose.bundle.yml up -dHNSW tuning:
# Increase ef_search for better recall (but slower)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SET hnsw.ef_search = 100"
# Or decrease for faster queries (but lower recall)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SET hnsw.ef_search = 40"
# Make permanent by adding to postgresql.confVacuum and analyze:
# Update table statistics
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"VACUUM ANALYZE notes"
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"VACUUM ANALYZE note_embeddings"Symptom: Database size grows unexpectedly large.
Diagnosis:
# Check total database size
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT pg_size_pretty(pg_database_size('matric'))"
# Check table sizes
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT tablename, pg_size_pretty(pg_total_relation_size(tablename::regclass))
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(tablename::regclass) DESC"
# Check for orphaned blobs
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT COUNT(*) FROM blobs WHERE id NOT IN (SELECT blob_id FROM note_blobs)"Fix:
Clean orphaned blobs:
# Delete orphaned blobs (blobs not referenced by any note)
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"DELETE FROM blobs WHERE id NOT IN (SELECT blob_id FROM note_blobs)"
# Reclaim space
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"VACUUM FULL blobs"Manage note versions:
# Check version count
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT note_id, COUNT(*) as version_count
FROM note_versions
GROUP BY note_id
ORDER BY version_count DESC
LIMIT 10"
# Delete old versions (keep last 10 per note)
# WARNING: This is destructive
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"DELETE FROM note_versions
WHERE id NOT IN (
SELECT id FROM (
SELECT id, ROW_NUMBER() OVER (PARTITION BY note_id ORDER BY created_at DESC) as rn
FROM note_versions
) sub WHERE rn <= 10
)"
# Reclaim space
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"VACUUM FULL note_versions"Vacuum full database:
# WARNING: This locks tables and can take time
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"VACUUM FULL ANALYZE"Symptom: Job queue has hundreds of pending jobs that never complete.
Diagnosis:
# Check job queue status
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT job_type, status, COUNT(*)
FROM job_queue
GROUP BY job_type, status
ORDER BY job_type, status"
# Check Ollama availability
curl http://localhost:11434/api/tags
# Check worker thread count
docker exec Fortémi-matric-1 printenv WORKER_THREADS
# Check for stuck jobs
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"SELECT id, job_type, status, attempts, error, updated_at
FROM job_queue
WHERE status = 'running'
AND updated_at < NOW() - INTERVAL '5 minutes'"Fix:
Ollama unavailable:
# See "Ollama Connection Failed" section above
# Ensure Ollama is running and reachable from containerIncrease worker threads:
# Edit docker-compose.bundle.yml or .env:
environment:
- WORKER_THREADS=4
# Restart container
docker compose -f docker-compose.bundle.yml restart matricStale jobs after container restart:
Since v2026.2.10, the worker automatically reaps orphaned running jobs on startup (threshold: 10 minutes). If you restarted recently and see stuck jobs, wait for the worker to start — it will reset them automatically and log: "Reaped stale running jobs from previous worker count=N".
If you need to reset jobs immediately (before the auto-reap threshold):
docker exec fortemi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue
SET status = 'pending', retry_count = retry_count + 1
WHERE status = 'running'
AND started_at < NOW() - INTERVAL '5 minutes'"Reset stuck jobs (legacy, pre-v2026.2.10):
# Reset jobs stuck in "running" state
docker exec fortemi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue
SET status = 'pending', attempts = 0
WHERE status = 'running'
AND updated_at < NOW() - INTERVAL '5 minutes'"Clear failed jobs:
# Reset failed jobs to retry
docker exec Fortémi-matric-1 psql -U matric -d matric -c \
"UPDATE job_queue
SET status = 'pending', error = NULL, attempts = 0
WHERE status = 'failed'"Symptom: WebSocket connections to /api/v1/ws disconnect after a few seconds.
Diagnosis:
# Test direct WebSocket connection (bypassing nginx)
websocat ws://localhost:3000/api/v1/ws
# Check nginx error logs
sudo tail -f /var/log/nginx/error.logFix:
Nginx not configured for WebSocket upgrade:
# Add these headers to your nginx location block for the API:
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s; # Keep alive for 24h
# ... other headers
}Proxy timeout too short:
# Increase timeouts for long-lived connections
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;Symptom: Connected to /api/v1/events but no events arrive (only keepalive).
Diagnosis:
# Check if events are being emitted
curl -N http://localhost:3000/api/v1/events
# You should see keepalive every 15 seconds:
# : keepalive
# Create a note to trigger events:
curl -X POST http://localhost:3000/api/v1/notes \
-H "Content-Type: application/json" \
-d '{"content": "test"}'Fix:
No activity in the system:
- QueueStatus events only emit when there are subscribers AND the job queue has activity
- Create or update a note to trigger NoteUpdated and job events
Buffering by reverse proxy:
# Disable buffering for SSE endpoints
location /api/v1/events {
proxy_pass http://localhost:3000;
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
}Symptom: Webhook deliveries failing (check via GET /api/v1/webhooks/:id/deliveries).
Diagnosis:
# List webhook deliveries
curl http://localhost:3000/api/v1/webhooks/<id>/deliveries?limit=10
# Test webhook manually
curl -X POST http://localhost:3000/api/v1/webhooks/<id>/testFix:
Target URL unreachable:
- Verify the webhook URL is accessible from the Fortémi container
- Check firewall rules and DNS resolution
- Webhook delivery timeout is 10 seconds
HMAC signature mismatch:
- Ensure your webhook receiver validates the
X-Fortemi-Signatureheader using HMAC-SHA256 with the configured secret - The signature is computed over the raw JSON body
Webhook not receiving expected events:
# Check which events the webhook is subscribed to
curl http://localhost:3000/api/v1/webhooks/<id>
# Update event filter
curl -X PATCH http://localhost:3000/api/v1/webhooks/<id> \
-H "Content-Type: application/json" \
-d '{"events": ["JobFailed", "JobCompleted", "NoteUpdated"]}'If you cannot resolve the issue with this guide:
Check logs for detailed error messages:
docker compose -f docker-compose.bundle.yml logs -fInteractive API documentation:
- OpenAPI UI:
http://localhost:3000/docs - OpenAPI spec:
http://localhost:3000/openapi.yaml
Report issues:
- Repository issues:
https://github.com/fortemi/fortemi/issues - Include: error message, relevant logs, Docker/system info, steps to reproduce
Additional documentation:
- Operators Guide - Deployment and maintenance procedures
- Configuration Reference - All environment variables and settings
- Embedding Model Selection - Choosing embedding models
- Real-Time Events - SSE, WebSocket, and webhook event streaming
For routine maintenance and monitoring, see the Operators Guide. For advanced configuration, see the Configuration Reference.