0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LiteLLMのUIで追加したモデルだけ401になる — os.environ/参照はconfig.yaml読み込み時にしか解決されない

0
Last updated at Posted at 2026-09-11

LLM gatewayの特定系統のモデルだけが401を返し始め、API keyが失効したものと思い込んで約5日溶かした。原因はkeyでもlitellmの障害でもなく、管理UIでモデルを追加したときにapi_key欄へ書いた os.environ/ZAI_API_KEY という参照が、その経路では一度も解決されないことにあった。

この記事を読み終えた時、手元に残るもの:

  1. os.environ/参照が解決される経路とされない経路の見分け方 (ソースの該当関数つき)
  2. DBに解決漏れのリテラルが残っていないかを数秒で確認する1クエリ
  3. 「同じkeyなのに一部のendpointだけ200」のような観測の正しい読み方

症状 — 追加したモデルだけが401

複数モデルを載せたlitellm proxyで、ある系統のモデル (glm系) のみ401を返し始めた。同じAPI keyを参照しているはずの、config.yamlに直書きしたモデルは同時刻に200を返している。

両者の差は1つだった。401のモデルは管理UIの「Add model」(内部的には POST /model/new) から追加したもので、config.yamlを編集したのではなかった。

環境

  • LiteLLM Proxy 1.83.10 (公式Docker image main-stable)
  • postgresを接続し STORE_MODEL_IN_DB: True — UIで追加したモデルはDBに保存される
  • api_key欄にはkey本体を書きたくないため os.environ/ZAI_API_KEY と入力した

原因 — envテンプレートの解決はconfig.yaml読み込み経路でだけ走る

litellmの公式docsには「Load API Keys / config values from Environment」という節があり、api_key: os.environ/MY_KEY の書式を紹介したうえで This works for ANY value on the config.yaml と書かれている。この一文は正確で、適用範囲の書き方も正確だ — config.yamlに対してのみ動く。

1.83.10のソースを読むと、モデル定義がrouterに載る経路は2つあり、os.environ/ の解決が行われるのは片方だけだった。

経路A: config.yaml (解決される)

起動時の設定読み込み get_config は、ファイルの設定にDBの設定をマージしてから _check_for_os_environ_vars を通す。この関数が文字列値を再帰的に走査し、os.environ/ 接頭辞を見つけたら実環境変数の値へ置き換える。docsの「ANY value」はこの実装のことを指している。

ただしここでDBからマージされるのは general_settings / router_settings / litellm_settings / environment_variables の4キーだけ。UIで追加したモデル行は、このマージには入らない。

経路B: DBのモデル行 (解決されない)

UIで追加したモデルは別の経路を通る。

  1. add_new_model (model_management_endpoints.py) が、UIから受け取った litellm_params を暗号化してそのままDBへ保存する。env参照を検査する処理はない
  2. 反映時はDBの行を読み、復号だけを行う decrypt_model_list_from_db / _add_deployment を通してrouterへupsertされる

経路Bのどこにも _check_for_os_environ_vars は現れない。追加直後の話だけではない — プロキシ再起動時もDBのモデル行は同じ復号のみの経路でrouterに載るので、再起動しても直らない。

結果、api_keyの値は os.environ/ZAI_API_KEY という文字列のままAuthorizationヘッダーに載り、上流はkeyが来ていないものとして401を返す。

DBにはその証拠が残っている。次の1クエリで数秒で確認できる:

select model_name
from "LiteLLM_ProxyModelTable"
where litellm_params->>'api_key' like 'os.environ/%';

このクエリが1行でも返したら、そのモデルはリテラル文字列をkeyとして送っている。値そのものは出力しない作りにしてある (keyを画面に出したくないため)。

試したが直らなかったこと (と、効かないと分かる理由)

  • 「keyの失効」説 — 401の文言から最初に疑った。同じkeyが中国向けendpoint (open.bigmodel.cn) のchatで200を返す観測と、後述のYAML移設後に同じkeyが通った事実で崩れた。keyは失効していなかった
  • 再起動 — 効かない。再起動時もDBのモデル行は経路B (復号のみ) でrouterに載るため
  • UIで追加し直す — 同じ経路をもう一度通るだけなので同じ結果になる。直る見込みがないことは原因節から読み取れる
  • endpointの向き先だけ修正 — 国際サブスクリプション側のendpoint (api.z.ai/api/coding/paas/v4) へ向きを直しても、Authorizationヘッダーに載っているのはリテラル文字列のままなので401は続く。両方直して初めて復旧した

なぜ気づけなかったか — 同じkeyで200になる別の面

切り分けを難しくした観測が1つある。このプロバイダーには2つのAPI面があり、手元のrelayの既定値は中国向けendpointを向いていた。国際サブスクリプションのkeyでも、中国面のchat呼び出しには200が返る。

「keyが有効なのに401?」— この観測はkey失効説を疑わしく見せる材料になった。だが実際には面が違うだけで、国際面から見た課金の整合とは無関係な観測だった。

同じkeyで一部のendpointだけ通るとき、それは「keyは有効」の証明ではなく「その面では有効」の証明として読む。endpointが複数面あるプロバイダーでは、対照実験の前に「同じ面を叩いているか」を確認する必要がある。

解決

  1. glm系のモデル定義をconfig.yamlへ移設した (api_key: os.environ/ZAI_API_KEY — 経路Aを通るので解決される)
  2. DBに残ったshadow 5行を削除した (同名モデルがYAMLとDBの両方にあると、DB行が生き続ける)
  3. プロキシを再起動した

解決を確認した方法

5点でe2eを流し、全て200を確認した。

  • 直指定2モデル (glm-5.2-direct / glm-5.3-direct)
  • alias (glm-5.2 / glm-5.3)
  • 既定モデル (default)
  • 対照に他ベンダーのモデル (minimax) — 修正が原因で別のモデルを壊していないことの確認

再発防止の運用は2択に整理した。

  • UIで追加する: api_key欄には素のkeyを書く。DB保存時はlitellmが暗号化するが、envテンプレートの利点 (設定ファイルや画面にkeyを書かない) は失われる
  • envテンプレートを使う: 全モデルをconfig.yamlで管理し、UIからの追加はしない

今回は後者を選んだ。gatewayの設定はコードと同じ扱い (変更はcommitとレビューを通す) にしたい運用方針とも合う。

限界

  • 読んだソースは1.83.10 (2026-09時点のmain-stable)。将来のversionで経路が変わる可能性がある
  • 「UI追加経路で解決されない」は、ソースの経路実読と実挙動 (DB行へのリテラル残存・401・YAML移設による復旧) からの組み合わせで確定した。Authorizationヘッダーの実バイトは記録していない (keyが載るものをログに残さない運用のため)
  • DB保存時の暗号化の実装詳細 (鍵の管理) は今回踏んでいない

gateway配下のモデルを、実タスクの記録で選定・振り分けする手順は別記事にまとめてある — LLMはベンチマークで選ばない — 自業務の実タスクで60分比較する手順

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?