---
title: "v1.103.1 - UI and CLI Session Token Format"
url: "/release_notes/v1.103.1/v1-103-1"
canonical_url: "https://docs.litellm.ai/release_notes/v1.103.1/v1-103-1"
type: "release-notes"
last_updated: "2026-10-02"
related:
  - "/release_notes/v1.103.2/v1-103-2"
  - "/release_notes/v1.103.0/v1-103-0"
---
# v1.103.1 - UI and CLI Session Token Format

> Index of all LiteLLM docs: https://docs.litellm.ai/llms.txt

## Deploy this version

**Docker**

```bash
docker run \
-e LITELLM_MASTER_KEY=sk-<paste-a-long-random-key> \
-e DATABASE_URL=postgresql://<user>:<password>@<host>:5432/<dbname> \
-e STORE_MODEL_IN_DB=True \
-p 4000:4000 \
docker.litellm.ai/berriai/litellm:1.103.1
```

**Pip**

```bash
pip install litellm==1.103.1
```

This release is published as [`ghcr.io/berriai/litellm:v1.103.1`](https://github.com/BerriAI/litellm/pkgs/container/litellm). See the [GitHub release](https://github.com/BerriAI/litellm/releases/tag/v1.103.1) and the full [releases page](https://github.com/BerriAI/litellm/releases)

:::danger Breaking Changes

**Upgrading from `v1.102.x` or earlier adds an index on `LiteLLM_SpendLogs`, which blocks spend log writes while it builds at startup.** On a large table that can take a long time. To avoid this, build the index yourself before upgrading with `CONCURRENTLY`, which does not block writes. The migration then finds it and skips the build. See [PR #37983](https://github.com/BerriAI/litellm/pull/37983)

```sql
SET statement_timeout = 0;
CREATE INDEX CONCURRENTLY IF NOT EXISTS "LiteLLM_SpendLogs_api_key_startTime_idx" ON "LiteLLM_SpendLogs"("api_key", "startTime");
```

Run it outside a transaction, from a session that stays connected until it finishes. A failed concurrent build leaves an invalid index behind, and the migration would skip that too, so check it before upgrading. This must return `true`, otherwise run `DROP INDEX CONCURRENTLY "LiteLLM_SpendLogs_api_key_startTime_idx";` and build it again:

```sql
SELECT indisvalid FROM pg_index WHERE indexrelid = '"LiteLLM_SpendLogs_api_key_startTime_idx"'::regclass;
```

If your spend logs table is partitioned, Postgres cannot build its index concurrently. Build a matching index concurrently on each partition first, then run the same `CREATE INDEX` without `CONCURRENTLY` on the parent, which only attaches them

**Session tokens issued before the upgrade stop working.** Admin UI and `lite` CLI users sign in once more after upgrading. During a rolling upgrade, pods on the old and new versions reject each other's session tokens, so finish the rollout before asking users to sign in again. Virtual keys, the master key and stored credentials are unaffected. See [`a80eaca`](https://github.com/BerriAI/litellm/commit/a80eacacee118fbed54e3d98f239d17a63fac818)

:::

:::warning `lite` CLI users must log in again

After upgrading the proxy, every `lite` CLI user has to run `lite login` once more. Until they do, the CLI keeps sending its old session token and its requests to the proxy fail

:::

`v1.103.1` is a patch release on top of [`v1.103.0`](https://github.com/BerriAI/litellm/releases/tag/v1.103.0). It carries one change: the session tokens the Admin UI and the `lite` CLI receive after sign-in now use their own encryption context and a header-safe format. Both the Docker image and the PyPI package were built from [`580bde9`](https://github.com/BerriAI/litellm/commit/580bde9a2d148714889ec1c04a9872819e78a778)

## UI and CLI session tokens get their own format

Session tokens were encrypted with the same routine the proxy uses for stored credentials, so they carried a `v2:gcm:` prefix and base64 padding. Basic auth parsers split on the first `:` and browsers reject `:` and `=` in WebSocket subprotocols, so Langfuse pass-through and the realtime playground could not use them. About one login in 262,144 also produced a token starting with `sk-`, which the proxy then treated as a virtual key and rejected with a 401

Session tokens are now AES-256-GCM encrypted under a context of their own and returned as `litellm_login_` followed by unpadded base64url. They pass through any header, are easy to spot in logs, and are checked only as session tokens. Stored credentials keep their current encryption, so there is nothing to migrate

### What's Changed

- refactor(auth): bind UI/CLI session tokens to their own AES-GCM context - [`a80eaca`](https://github.com/BerriAI/litellm/commit/a80eacacee118fbed54e3d98f239d17a63fac818)
- chore(lint): scope a TRY004 suppression to the bearer-token salt key check - [`d3bf006`](https://github.com/BerriAI/litellm/commit/d3bf0066132899ca86fce126a11f4c133d9a74c8)

## Full Changelog

https://github.com/BerriAI/litellm/compare/v1.103.0...v1.103.1

## Related pages

- [v1.103.2](https://docs.litellm.ai/release_notes/v1.103.2/v1-103-2.md)
- [v1.103.0](https://docs.litellm.ai/release_notes/v1.103.0/v1-103-0.md)
