# ClaimManager AI 403 — exact diagnosis and recovery

## Confirmed root causes in this build

1. **Credential/endpoint mismatch (certain).** The configured key is an `AQ.*` Google Cloud Express Mode key, but `LLM_BASE_URL` is the Gemini Developer API OpenAI-compatible endpoint (`generativelanguage.googleapis.com/v1beta/openai`). An Express Mode key is not a Bearer credential for that endpoint.
2. **The two failover entries are identical (certain).** `LLM_API_KEY` and `LLM_FALLBACK_API_KEY` contain the same credential and use the same endpoint. This is not failover; it repeats the same permanent failure.
3. **The backend egress appears to be in Iran (very likely).** Google does not list Iran as an available region for Google AI Studio / Gemini Developer API. A generic HTML `Error 403 (Forbidden)` page, rather than the Gemini JSON error schema, is consistent with an edge/regional block.
4. **Primary model discovery used the wrong URL (code defect).** Generation used `LLM_BASE_URL`, but model discovery guessed `https://api.openai.com/v1` for the primary profile. This produced the unrelated OpenAI timeout in the log.
5. **The AI SDK's HTTP status was discarded (code defect).** For HTML error bodies, the SDK message is only `Failed to process error response`; the HTTP status remains in `APICallError.statusCode`. The old code read only `.message`, so a 403 was not classified until a later model happened to produce the word `Forbidden`.

`gemini-3.6-flash` itself is not the primary fault in this snapshot: Google's current Gemini OpenAI-compatibility documentation uses that exact model ID.

## Security action — do this first

The archive contains a real `.env` file. Treat every key in it as exposed:

1. Revoke/delete both current entries in the provider console.
2. Create a fresh credential only after selecting one of the supported routes below.
3. Never distribute `.env`; distribute `.env.example` only.

## Select exactly one supported route

### Route A — OpenAI-compatible provider reachable from the backend (fastest local route)

Use a provider that is legally available from the backend's actual network and exposes an OpenAI-compatible API. Obtain the exact base URL and model ID from that provider's panel or `/models` endpoint.

```dotenv
LLM_API_KEY=REPLACE_WITH_NEW_PROVIDER_KEY
LLM_BASE_URL=https://PROVIDER_HOST/v1
LLM_PROVIDER=provider-name
LLM_MODEL_PRO=EXACT_MODEL_ID_FROM_PROVIDER
LLM_MODEL_FREE=EXACT_MODEL_ID_FROM_PROVIDER
LLM_MODEL_FALLBACK=EXACT_SECOND_MODEL_ID_FROM_PROVIDER

# Do not configure a fallback unless it is genuinely independent.
# LLM_FALLBACK_API_KEY=
# LLM_FALLBACK_BASE_URL=
```

Do not copy Google model IDs into another provider unless that provider explicitly lists those IDs.

### Route B — Gemini Developer API from a supported backend region

Run the **server/Bun process** in a Google-supported region, under an account and deployment that comply with Google's terms. A browser-only VPN does not move server-side `*.server.ts` traffic.

Create a fresh **Gemini Developer API** credential for the Gemini API. Do not reuse an `AQ.*` Express Mode key.

```dotenv
LLM_API_KEY=REPLACE_WITH_NEW_GEMINI_DEVELOPER_API_KEY
LLM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
LLM_PROVIDER=ai-studio
LLM_MODEL_PRO=gemini-3.6-flash
LLM_MODEL_FREE=gemini-3.6-flash
LLM_MODEL_FALLBACK=gemini-flash-latest

# Leave these absent unless they point to a truly different working credential.
# LLM_FALLBACK_API_KEY=
# LLM_FALLBACK_BASE_URL=
```

### Route C — keep the current `AQ.*` Express Mode credential

This requires a native Google Gen AI integration, for example `new GoogleGenAI({ vertexai: true, apiKey })`, using the Agent Platform Express Mode REST surface. It cannot be fixed by merely changing `LLM_BASE_URL` in the current OpenAI-compatible adapter. Agent Platform's OpenAI-compatible endpoint accepts Google Cloud OAuth access tokens, not the `AQ.*` key as a Bearer token.

This route is a code migration, not an `.env` edit. It also remains subject to Google Cloud availability, account, and regional requirements.

## Clean restart and verification (Windows PowerShell)

From the project directory:

```powershell
# Stop the existing Vite/Bun process first with Ctrl+C.
# Edit .env using exactly one route above.

bun run check:ai
```

Do not start the UI until the check ends with:

```text
✓ آماده است. برنامه را با bun run dev اجرا کنید.
```

Then start a fresh process:

```powershell
bun run dev
```

A restart matters because cooldown state is kept in server-process memory. After restart, test with one or two small documents before a large batch.

## Expected diagnostics

- `HTML Error 403 (Forbidden)`: request blocked before a normal Gemini JSON response; check region/network route first.
- JSON `USER_LOCATION_NOT_SUPPORTED` / `Access Restricted`: unsupported region.
- JSON `SERVICE_DISABLED`: enable the Generative Language API for the credential's project.
- JSON `API_KEY_INVALID`: wrong/revoked key or wrong credential type.
- JSON `API_KEY_SERVICE_BLOCKED` / `project has been denied access`: create an eligible fresh credential/project or contact provider support.
- HTTP 404: model ID or base URL is wrong; use the provider's `/models` result.
- HTTP 429: quota/rate limit; reduce batch size or use a genuinely independent paid/fallback route.

## Code changes included in the hotfix

- Primary model discovery now uses the same resolved endpoint as generation.
- Duplicate key+endpoint profiles are ignored.
- `APICallError.statusCode`, response body, and cause are retained for classification.
- Model discovery logs non-2xx status and a bounded response snippet.
- `check:ai` rejects impossible `AQ.*` + Gemini Developer API configurations before making requests.
- `check:ai` exits through `process.exitCode`, avoiding Bun's Windows `UV_HANDLE_CLOSING` shutdown assertion.
- `.env.example` no longer advertises the invalid Express Mode/OpenAI pairing.
