Troubleshooting
This guide covers the most common issues encountered when running Contract Lucidity, along with their root causes and solutions.
Common Issues
"type vector does not exist"
Symptom: Backend fails to start, Alembic migration errors with:
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedObject)
type "vector" does not exist
Cause: The pgvector extension is not installed in the PostgreSQL database.
Solution: This is auto-resolved on startup since commit 439fef0. The backend lifespan handler runs CREATE EXTENSION IF NOT EXISTS vector before migrations. If you still see this error:
- Verify you are using the
pgvector/pgvector:pg16Docker image (not vanillapostgres:16) - Manually create the extension:
docker exec cl-postgres psql -U cl_user -d contract_lucidity \
-c "CREATE EXTENSION IF NOT EXISTS vector;"
- Restart the backend:
docker compose restart cl-backend
The pgvector/pgvector:pg16 image includes the vector extension binaries. The standard postgres:16 image does not. If you are using a managed PostgreSQL service (RDS, Azure, Cloud SQL), ensure pgvector is enabled in the extensions settings.
"could not translate host name cl-postgres"
Symptom: Backend or worker cannot connect to the database:
could not translate host name "cl-postgres" to address:
Name or service not known
Cause: The POSTGRES_HOST environment variable is set to the Docker service name (cl-postgres), but the service is not reachable -- either because:
- The container is not running
- The service is on a different Docker network
- You are running outside Docker (e.g., local development) and
cl-postgresdoes not resolve
Solution:
# 1. Check if cl-postgres is running
docker ps --filter "name=cl-postgres"
# 2. If not running, start it
docker compose up -d cl-postgres
# 3. If running outside Docker, set POSTGRES_HOST to localhost
export POSTGRES_HOST=localhost
If using an external database (managed PostgreSQL), set POSTGRES_HOST to the database hostname in your .env:
POSTGRES_HOST=your-db-server.postgres.database.azure.com
Documents Stuck in Queue
Symptom: Documents remain in QUEUED status indefinitely. The UI shows a spinning indicator that never progresses.
Cause: The Celery worker is either not running or cannot connect to Redis.
Diagnosis:
# 1. Check if the worker is running
docker ps --filter "name=cl-worker"
# 2. Check worker logs for errors
docker logs --tail 50 cl-worker
# 3. Check Redis connectivity from the worker
docker exec cl-worker python -c "import redis; r = redis.from_url('redis://cl-redis:6379/0'); print(r.ping())"
# Expected: True
# 4. Check queue depth
docker exec cl-redis redis-cli LLEN celery
Solution:
# If worker is not running
docker compose up -d cl-worker
# If worker can't connect to Redis, check REDIS_URL / CELERY_BROKER_URL in .env
# Default: redis://cl-redis:6379/0
# If worker is running but tasks aren't being picked up, restart it
docker compose restart cl-worker
After resolving the issue, stuck documents do not automatically retry. You must reprocess them via the UI (click the document, then Reprocess) or via the API.
"Package not found at /data/storage/..."
Symptom: Pipeline fails at the extraction stage with a file-not-found error:
FileNotFoundError: [Errno 2] No such file or directory: '/data/storage/projects/abc/doc-123.pdf'
Cause: The backend and worker containers do not share the same storage volume. The backend saved the uploaded file to its /data/storage, but the worker's /data/storage points to a different (or empty) volume.
Solution: Ensure both cl-backend and cl-worker mount the same cl-storage volume:
# docker-compose.yml -- both services must have this
volumes:
- cl-storage:/data/storage
Verify:
# Upload a test file via the UI, then check it exists in the worker
docker exec cl-worker ls -la /data/storage/
If you are running multiple worker instances across different hosts, you must use a shared filesystem (NFS, EFS, Azure Files) for /data/storage. Local Docker volumes are not shared across hosts.
Pipeline Fails at Extraction
Symptom: Documents fail at the extracting stage with errors like:
RuntimeError: No text extracted from contract.pdf
Cause: The uploaded file is corrupt, password-protected, or in an unsupported format. While CL's file validation rejects most invalid files at upload time, some edge cases slip through:
- Scanned PDFs with no OCR layer (Tesseract OCR handles most, but very poor scans may fail)
- Password-protected or DRM-encrypted PDFs
- Files with the wrong extension (e.g., a
.docfile renamed to.pdf) - Extremely large files that exceed memory during extraction
Solution:
- Check the specific error in the worker logs:
docker logs cl-worker 2>&1 | grep "FAILED at extracting"
- Try opening the file manually to verify it is valid
- For scanned PDFs, ensure Tesseract OCR is installed in the worker container (it is by default in the standard Docker image --
tesseract-ocrandtesseract-ocr-eng) - For password-protected files, remove the password and re-upload
- Check the
MAX_UPLOAD_SIZE_MBsetting (default: 100 MB)
502 on AI Draft
Symptom: Clicking "AI Draft" or "Generate Suggestion" in the clause review UI returns a 502 Bad Gateway error.
Cause: The AI provider is not configured or the API key is invalid. The backend attempts an AI call, receives an error from the provider, and the request times out or fails.
Diagnosis:
# Check backend logs for AI errors
docker logs --tail 100 cl-backend 2>&1 | grep -i "AIConfigError\|api_key\|401\|403"
Solution:
- Log in as an administrator
- Navigate to Settings > AI Providers
- Verify a provider is configured and active
- Click Verify to test the API key
- Go to Settings > AI Capabilities and ensure the Generation capability has a default mapping
Common API key issues:
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Key expired or revoked | Regenerate key at provider console |
403 Forbidden | Key does not have required permissions | Check key scopes/permissions |
429 Too Many Requests | Rate limit exceeded | Wait and retry, or upgrade tier |
AIConfigError: No active AI provider configured | No mapping for the capability | Configure in Settings > AI Capabilities |
Duplicate Pages in Raw Text
Symptom: The extracted text for a document contains duplicated content -- every page appears twice.
Cause: This was a reprocessing bug where the extraction stage appended new pages without clearing existing rows. This has been fixed -- the extraction stage now clears existing DocumentRawText rows before inserting new ones:
# From tasks.py - _stage_extract()
# Clear any existing raw text from previous pipeline runs (reprocessing)
existing_raw = session.execute(
select(DocumentRawText).where(DocumentRawText.document_id == doc.id)
).scalars().all()
for er in existing_raw:
session.delete(er)
Solution: If you encounter this on an older version, reprocess the affected document. On current versions, this should not occur.
Frontend Not Updating After Code Changes
Symptom: You deployed new frontend code but the UI still shows the old version.
Cause: Docker volume mounts and HMR caching can cause stale builds.
Solution:
# For source file changes (src/, public/) -- volume-mounted, just restart
docker compose restart cl-frontend
# For config changes (next.config.ts) -- NOT volume-mounted, requires rebuild
docker compose up -d --build cl-frontend
# Nuclear option: full rebuild with no cache
docker compose down cl-frontend
docker compose build --no-cache cl-frontend
docker compose up -d cl-frontend
Browser extensions (e.g., LastPass's cz-shortcut-listen attribute) can cause harmless hydration warnings in the console. These are not CL bugs and can be safely ignored.
Alembic Migration Conflicts
Symptom: Backend fails to start with Alembic errors about conflicting migration heads or missing revisions.
Cause: Multiple developers created migrations from the same parent, or a migration was manually deleted.
Solution:
# Check current migration state
docker exec cl-backend alembic history --verbose
docker exec cl-backend alembic current
# If there are multiple heads, merge them
docker exec cl-backend alembic merge heads -m "merge migration branches"
# If the migration table is corrupted, stamp to the latest
docker exec cl-backend alembic stamp head
docker compose restart cl-backend
How to Check Service Logs
All Services
# Follow all CL container logs in real-time
docker compose logs -f
# Follow specific service logs
docker compose logs -f cl-backend
docker compose logs -f cl-worker
docker compose logs -f cl-frontend
docker compose logs -f cl-postgres
Filtered Log Queries
# Find all pipeline failures in the last hour
docker logs --since 1h cl-worker 2>&1 | grep "FAILED"
# Find AI-related errors
docker logs --since 1h cl-backend 2>&1 | grep -i "AIConfigError\|api_key\|rate.limit"
# Find database connection errors
docker logs --since 1h cl-backend 2>&1 | grep -i "connection\|pg_\|postgres"
# Count documents processed today
docker logs --since 24h cl-worker 2>&1 | grep "pipeline COMPLETE" | wc -l
How to Reprocess a Failed Document
Via the UI
- Navigate to the project containing the failed document
- Click on the document (it will show a red "Failed" status)
- Click the Reprocess button
- The document re-enters the pipeline from the beginning
Via the API
# Get the document ID from the UI URL or database
DOCUMENT_ID="your-document-uuid"
TOKEN="your-jwt-token"
curl -X POST "https://contractlucidity.com/api/documents/${DOCUMENT_ID}/reprocess" \
-H "Authorization: Bearer ${TOKEN}"
Via the Database (Emergency)
# Reset a stuck document to QUEUED status so the worker picks it up
docker exec cl-postgres psql -U cl_user -d contract_lucidity \
-c "UPDATE documents SET pipeline_status = 'queued', failed_at_stage = NULL, error_message = NULL WHERE id = 'your-document-uuid';"
# Then trigger the Celery task manually
docker exec cl-worker python -c "
from app.pipeline.tasks import process_document
process_document.delay('your-document-uuid')
"
Reprocessing a document that has already been analysed will replace the existing analysis. The previous clauses, embeddings, and report will be overwritten. Reprocessing is designed for failed documents and transient errors, not for re-analysing completed documents.
Diagnostic Quick Reference
| What to Check | Command |
|---|---|
| All container status | docker ps --filter "name=cl-" |
| Backend health | curl -s localhost:8000/api/health |
| Worker alive | docker exec cl-worker celery -A app.celery_app inspect ping |
| Redis alive | docker exec cl-redis redis-cli ping |
| PostgreSQL alive | docker exec cl-postgres pg_isready -U cl_user |
| Queue depth | docker exec cl-redis redis-cli LLEN celery |
| Failed documents | docker exec cl-postgres psql -U cl_user -d contract_lucidity -c "SELECT count(*) FROM documents WHERE pipeline_status = 'failed';" |
| Recent errors | docker logs --since 1h cl-worker 2>&1 | grep FAILED |
| Disk usage | docker system df |
| Database size | docker exec cl-postgres psql -U cl_user -d contract_lucidity -c "SELECT pg_size_pretty(pg_database_size('contract_lucidity'));" |