# 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 <token>`
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.
