# Troubleshooting ## Application will not start ### `Could not resolve placeholder 'X'` ``` java.lang.IllegalArgumentException: Could not resolve placeholder 'OPENAI_API_KEY' in value "${OPENAI_API_KEY}" ``` A required environment variable is missing. This is the intended behaviour — the service refuses to start rather than run with an unusable credential. **Fix:** set the named variable. Locally that means `.env`; in production, `.env.production` on the deployment host followed by `docker compose restart parser-service`. [`.env.example`](../.env.example) lists every variable and marks which are required. Check what the container actually received: ```bash docker compose exec parser-service env | grep -E 'MONGODB|OPENAI|MINIO|JWT' ``` ### MongoDB connection refused / timeout The service cannot operate without MongoDB, so this is fatal at startup. ```bash ./scripts/smoke-mongodb.sh # reachability + configured connection string ``` Work through, in order: 1. **Reachability** — `nc -z $MONGODB_HOST $MONGODB_PORT`. If this fails the problem is network or firewall, not credentials. 2. **Credentials** — `MONGODB_USERNAME` / `MONGODB_PASSWORD` are set and exported. 3. **Auth database** — `MONGODB_AUTH_DATABASE` must match how the user was created, normally `admin`. A user created against `admin` but authenticated against `parser_db` fails with an authentication error, not a connection error. 4. **Grants** — the user needs read/write on `MONGODB_DATABASE`. Timeouts are 30s for connect, socket and server selection. A slow first request after an idle period is usually server selection re-establishing the topology. Historical detail: [archive/setup-notes/MONGODB_TROUBLESHOOTING.md](archive/setup-notes/MONGODB_TROUBLESHOOTING.md). ## Runtime failures ### `401 Unauthorized` from OpenAI ``` OpenAI request failed: 401 Unauthorized from POST https://api.openai.com/v1/chat/completions ``` The key in `OPENAI_API_KEY` is invalid, revoked, or belongs to an organisation without access to the configured model. ```bash ./scripts/smoke-openai-api.sh # validates the key directly against the API ``` Then check the service's own view: `GET /api/openai/test`. If the key is fine but calls still fail, confirm `openai.model.name` (`gpt-4o-mini`) and `openai.model.name.text` (`gpt-4o`) are both available to your account. ### `401` from this service's own endpoints That is JWT validation, not OpenAI. `security.jwt.secret-base64` must be **byte-identical to the secret used by the auth service that issued the token**. A mismatch produces a signature failure on every request. Also confirm the header is `Authorization: Bearer ` and the token has not expired. ### Rate limiting / slow AI responses OpenAI calls retry with exponential backoff — up to 3 attempts normally, and up to 10 with a 5s–300s backoff specifically for rate-limit responses. Concurrency is capped at 3 requests. Sustained `429`s mean the cap is not low enough for your quota; lower `openai.rateLimit.maxConcurrentRequests`. Ollama timeouts are deliberately long (up to 5 hours) because it generates long-form report text on self-hosted hardware. A request that appears hung may simply be generating. ### Image or video generation returns null ``` [Imagen3] Credentials file not found: keys/google-key.json ``` The Google service-account key is missing from the build. It must exist at `src/main/resources/keys/google-key.json` **when the jar is built**, because it is loaded from the classpath. See [configuration.md](configuration.md#the-google-service-account-key). This degrades gracefully — only image and video generation are affected. ### Reports fail with a date `NullPointerException` ``` Cannot invoke "java.time.LocalDateTime.toLocalDate()" because "end" is null ``` `ReportGenerationService` defaults a missing range to the last 30 days. If you see this again, a new code path is formatting `startDate`/`endDate` without a null check. Background: [archive/changelogs/NULL_POINTER_FIX.md](archive/changelogs/NULL_POINTER_FIX.md). ### CORS errors in the browser Origins come from `CORS_ALLOWED_ORIGINS` and default to `https://konturai.kz` and `https://www.konturai.kz`. A local frontend needs its origin added explicitly — `cors.allow-credentials=true` means a wildcard origin is not permitted. ## Diagnostics ```bash curl -fsS localhost:8080/api/parser/health # liveness docker compose logs -f parser-service # follow logs docker compose logs --tail=200 parser-service | grep -i error ``` Raise log detail for a specific area by overriding its level, e.g. `logging.level.kz.konturai.parser.service.MarketingAnalysisService=DEBUG`. `MarketingController` and `MarketingAnalysisService` already log at `DEBUG`. Mongo driver logs are at `WARN` to keep the noise down — raise `logging.level.org.springframework.data.mongodb` to `DEBUG` when investigating queries. Heap dumps from OOM kills land in `/dumps/heap.hprof` inside the container.