Troubleshooting¶
Common issues and how to resolve them, grouped by area.
Database Issues¶
OperationalError: Connection refused at startup¶
Cause: The service started before PostgreSQL was ready.
Local fix: Docker Compose uses healthchecks — this should not happen. If it does:
docker compose restart auth-service # retry after DB is healthy
Render fix: The service's DATABASE_URL is wrong — likely a typo in the host or port. Verify the Neon connection string in the Render environment, then trigger a manual redeploy.
asyncpg.exceptions.InvalidAuthorizationSpecificationError¶
Cause: Wrong password in the connection string.
Fix: Re-copy the Neon connection string from Connection Details in the Neon console. Make sure you did not accidentally modify the password segment.
SSL SYSCALL error: EOF detected / ssl=require not recognized¶
Cause: asyncpg does not understand sslmode=require (Neon's default string uses this). You must use ssl=require instead.
Fix: In DATABASE_URL, replace ?sslmode=require with ?ssl=require.
# Wrong
postgresql+asyncpg://...?sslmode=require
# Correct
postgresql+asyncpg://...?ssl=require
Alembic migration fails at startup¶
Symptom: In the service logs you see alembic.util.exc.CommandError or relation "xxx" already exists.
Fix:
- Check if a partial migration ran: connect to Neon with psql and inspect
alembic_version - Run the migration manually to identify the failing step:
cd jinbocho-auth-v1 export DATABASE_URL="postgresql+asyncpg://...neon.tech/auth_db?ssl=require" alembic upgrade head - If the schema is in an inconsistent state, reset it:
# Drop and recreate the database in Neon console, then redeploy # (Alembic will rerun all migrations from scratch)
Service-to-Service Issues¶
Catalog service returns 401 for every request¶
Cause: JWT_SECRET_KEY on catalog-service does not match auth-service.
Verification: The JWT is signed by auth and verified by catalog. If the secrets differ, every token will fail validation silently.
Fix: Confirm that JWT_SECRET_KEY is exactly the same string in auth, catalog, and gateway environment variables on Render. Copy-paste errors (trailing spaces, newlines) are common.
Gateway returns 502 Bad Gateway¶
Cause: The gateway cannot reach an internal Private Service.
Checks:
- The internal service URL in the gateway env is wrong — copy it directly from the service's page in Render (not the public URL — Private Services have no public URL)
- The Private Service is still deploying or has crashed — check its logs
- The gateway and the internal service are in different Render regions — Private Services only communicate within the same region
AUTH_SERVICE_URL from catalog returns Connection refused¶
Cause: Catalog is using http://jinbocho-auth:8001 but the actual Render internal hostname is different.
Fix: On Render, the internal hostname for a service is shown on its service page. Use exactly that value — do not guess or construct it manually.
Frontend / CORS Issues¶
CORS error in browser console: Access-Control-Allow-Origin missing¶
Cause: The gateway's CORS_ORIGINS does not include the frontend URL.
Fix:
- Render → api-gateway → Environment
- Set
CORS_ORIGINSto the exact frontend origin:["https://jinbocho-fe.onrender.com"] - No trailing slash
- Must be a valid JSON array (double quotes, square brackets)
- Save and wait for redeploy
Frontend shows blank page after deploy¶
Cause: VITE_API_BASE_URL was not set at build time, so API calls go to the wrong URL.
Fix: On Render Static Site, verify VITE_API_BASE_URL is set to the gateway's public URL. Then click Clear build cache & deploy — Vite inlines env vars at build time, so a simple redeploy is not enough if the variable was added after the last build.
Login succeeds but all subsequent API calls return 401¶
Cause: The access token is being sent correctly but the gateway or a backend service is rejecting it.
Checks:
- Open browser DevTools → Network tab → inspect a failing request's
Authorizationheader - Decode the JWT at jwt.io — check
issandaudmatch what catalog expects (iss: jinbocho-auth,aud: jinbocho) - Confirm
JWT_ALGORITHMisHS256on all services
ISBN Lookup Issues¶
ISBN lookup returns 404 for a valid ISBN¶
Cause: The book is not in Open Library or Google Books, or the Google Books API key is missing/invalid.
Checks:
- Test Open Library directly:
curl "https://openlibrary.org/api/books?bibkeys=ISBN:9788845292613&format=json&jscmd=data" - If Open Library returns data but the service does not, check catalog-service logs for errors
- If
GOOGLE_BOOKS_API_KEYis missing, the fallback lookup is skipped — add the key to the Render environment
ISBN lookup is slow (> 2 seconds)¶
Cause: The ISBN is not in the local cache, and the external lookup is slow.
Fix: This is expected for the first lookup of any ISBN. Subsequent lookups for the same ISBN are served from the isbn_cache table and are fast. If external lookups are consistently slow, check your Neon region vs Render region latency.
Cold Starts (Free Tier)¶
First request after inactivity takes 30-60 seconds¶
Cause: Render free-tier services sleep after 15 minutes of inactivity. The first request wakes them up.
Options:
- Accept it — for a home library used by a few people, cold starts are tolerable
- Upgrade to Render Starter ($7/month per service) — services stay always-on
- Ping the gateway periodically — an external cron service (e.g. cron-job.org, free) can hit
/healthevery 10 minutes to keep the gateway warm. The Private Services behind it still cold-start, but the gateway responds instantly.
# Example: setup a free cron job to ping the gateway every 10 minutes
# URL: https://jinbocho-api-gateway.onrender.com/health
# Method: GET
# Schedule: */10 * * * *
Local Development Issues¶
Port already in use¶
# Find the process using port 8000
lsof -i :8000
# Kill it
kill -9 <PID>
# Or change the port in docker-compose.yml
mypy strict mode fails on a new module¶
If you add a new file and mypy --strict complains about missing type annotations:
# Run mypy and see specific errors
python -m mypy app --strict 2>&1 | grep error
# Common fixes:
# - Add return type annotation to every function
# - Add type annotations to all variables
# - Mark optional parameters with Optional[T] or T | None
ruff check fails after editing¶
# See exactly what failed
ruff check app tests
# Auto-fix what ruff can
ruff check --fix app tests
# For remaining issues, fix manually (ruff shows line numbers)