ダッシュボードのモデル選択ドロップダウンに未構成プロバイダーのモデルが表示される
OpenClaw ダッシュボードのAgentモデル選択ドロップダウンでは、プロバイダーの設定状態に関係なく、対応のすべてのモデルが表示されます。これにより、利用不可のモデルを選択したユーザーに混乱が生じます。
🔍 症状
ユーザーから報告された動作
Dashboard(http://localhost:8080)にアクセスし、Agent Settingsに移動すると、Primary Modelドロップダウンに広範なモデルのリストが表示されます:
┌─────────────────────────────────────────────────────────┐
│ Primary Model ▼ │
├─────────────────────────────────────────────────────────┤
│ 🔍 Search models... │
├─────────────────────────────────────────────────────────┤
│ ▼ Alibaba Cloud (bailian) │
│ bailian/kimi-k2.5 │
│ bailian/qwen-plus │
│ ▼ Anthropic │
│ anthropic/claude-opus-4-5 │
│ anthropic/claude-sonnet-4 │
│ ▼ Amazon Bedrock │
│ bedrock/anthropic.claude-3-5-sonnet │
│ bedrock/anthropic.claude-3-opus │
│ ▼ OpenAI │
│ openai/gpt-4o │
│ openai/gpt-4-turbo │
└─────────────────────────────────────────────────────────┘
失敗シナリオ
ユーザーが未設定のプロバイダー(例:anthropic/claude-opus-4-5)からのモデルを選択し、エージェントを保存または使用しようとすると:
$ openclaw agent update --model anthropic/claude-opus-4-5
[ERROR] Provider 'anthropic' is not configured.
Please configure credentials in config.yaml or set environment variables.
Run 'openclaw config list' to see current provider status.
技術的な症状
ブラウザコンソールで、ドロップダウンコンポーネントがsrc/models/supported-models.jsonからモデルを読み込み、プロバイダーの可用性を確認せずにレンダリングします:
[OpenClaw] Model list loaded: 127 models
[OpenClaw] Provider check skipped for dropdown population
🧠 原因
アーキテクチャ上の問題
Dashboardのモデル選択コンポーネント(src/components/ModelSelector.tsx)は、コンポーネントの初期化時に静的モデルレジストリからサポートされているモデルの完全なリストを読み込みます:
// src/components/ModelSelector.tsx - Line 23-45
function ModelSelector() {
const [models, setModels] = useState<Model[]>([]);
useEffect(() => {
// Current implementation - loads ALL models
const allModels = getSupportedModels();
setModels(allModels);
}, []);
// ... rest of component
}
プロバイダーの可用性チェックの欠落
コンポーネントはgetConfiguredProviders()や同等のものを呼び出して、プロバイダーの可用性によってモデルをフィルタリングしません。モデルレジストリとプロバイダー設定は別々のデータドメインに存在し、統合されていません:
┌─────────────────────────────────┐
│ Models Registry │
│ (src/models/supported-models.ts)│
│ │
│ • 127 models defined │
│ • Grouped by provider │
│ • No runtime validation │
└───────────────┬─────────────────┘
│
▼ NO INTEGRATION
┌─────────────────────────────────┐
│ Provider Configuration │
│ (config.yaml / env vars) │
│ │
│ • User-configured credentials │
│ • Provider availability state │
│ • NOT consulted by UI │
└─────────────────────────────────┘
データフローのギャップ
- ユーザーがDashboardを開く →
ModelSelectorコンポーネントがマウント - コンポーネントがすべてのモデルを取得 →
getSupportedModels()が完了な静的リストを返す - バックエンドへのAPI呼び出しなし → コンポーネントは
/api/providers/statusをクエリしない - ドロップダウンがすべてのモデルを表示 → 未設定のプロバイダーからのモデルも含む
- ユーザーが無効なモデルを選択 → 実行時にのみ런タイムエラーが発生
影響を受けるコードパス
src/
├── components/
│ └── ModelSelector.tsx ← Does not filter by provider config
├── services/
│ └── providerService.ts ← Contains getConfiguredProviders() but unused
└── models/
└── supported-models.ts ← Returns unfiltered model list
🛠️ 解決手順
Option A: 設定済みプロバイダーでモデルをフィルタリング(推奨)
src/components/ModelSelector.tsxを変更して、プロバイダーの可用性に基づいてモデルをフィルタリングします:
// src/components/ModelSelector.tsx - FIXED IMPLEMENTATION
import { useState, useEffect } from 'react';
import { getSupportedModels, Model } from '../models/supported-models';
import { getConfiguredProviders } from '../services/providerService';
function ModelSelector({ onModelSelect }: Props) {
const [models, setModels] = useState<Model[]>([]);
const [configuredProviders, setConfiguredProviders] = useState<Set<string>>(new Set());
const [showOnlyConfigured, setShowOnlyConfigured] = useState(true);
useEffect(() => {
async function loadData() {
// Step 1: Get list of configured providers
const providers = await getConfiguredProviders();
const configuredSet = new Set(providers.map(p => p.id));
setConfiguredProviders(configuredSet);
// Step 2: Load all models
const allModels = getSupportedModels();
// Step 3: Filter to only configured providers
const filteredModels = showOnlyConfigured
? allModels.filter(model => configuredSet.has(model.provider))
: allModels;
setModels(filteredModels);
}
loadData();
}, [showOnlyConfigured]);
// ... rest of component
}
Option B: 未設定のモデルに視覚的なインジケーターを追加
すべてのモデルの視認性を維持しながら視覚的な区別を行う場合:
// src/components/ModelSelector.tsx - ALTERNATIVE FIX
function ModelSelector({ onModelSelect }: Props) {
const [models, setModels] = useState<Model[]>([]);
const [configuredProviders, setConfiguredProviders] = useState<Set<string>>(new Set());
useEffect(() => {
async function loadData() {
const providers = await getConfiguredProviders();
setConfiguredProviders(new Set(providers.map(p => p.id)));
setModels(getSupportedModels());
}
loadData();
}, []);
const isProviderConfigured = (providerId: string) => {
return configuredProviders.has(providerId);
};
return (
<select onChange={(e) => onModelSelect(e.target.value)}>
{models.map(model => (
<option
key={model.id}
value={model.id}
disabled={!isProviderConfigured(model.provider)}
style={{
color: isProviderConfigured(model.provider) ? 'inherit' : '#999',
backgroundColor: isProviderConfigured(model.provider) ? 'white' : '#f5f5f5'
}}
>
{model.displayName}
{!isProviderConfigured(model.provider) && ' ⚠️ (provider not configured)'}
</option>
))}
</select>
);
}
Option C: バックエンド主導のフィルタリング
利用可能なモデルのみを返す新しいAPIエンドポイントを追加します:
// src/routes/api/models/available.ts
import { Router } from 'express';
import { getSupportedModels } from '../../models/supported-models';
import { getConfiguredProviders } from '../../services/providerService';
const router = Router();
router.get('/available', async (req, res) => {
const configuredProviders = await getConfiguredProviders();
const providerIds = new Set(configuredProviders.map(p => p.id));
const availableModels = getSupportedModels()
.filter(model => providerIds.has(model.provider));
res.json({
models: availableModels,
configuredProviders: configuredProviders.map(p => p.id)
});
});
export default router;
🧪 検証
検証手順
修正を適用した後、以下の方法で動作を確認します:
1. Dashboard UIの検証
# Start the Dashboard
$ openclaw dashboard start
# Open browser to http://localhost:8080
# Navigate to Agent Settings → Primary Model dropdown
#
# Expected: Only models from configured providers appear
# Example: If only bailian is configured, only bailian/* models show
2. APIエンドポイントの検証
# If using Option C (backend filtering)
$ curl http://localhost:8080/api/models/available | jq
# Expected output:
{
"models": [
{"id": "bailian/kimi-k2.5", "provider": "bailian", ...},
{"id": "bailian/qwen-plus", "provider": "bailian", ...}
],
"configuredProviders": ["bailian"]
}
# Should NOT contain anthropic, bedrock, openai models
3. コンポーネントのユニットテスト
# Run the ModelSelector component tests
$ npm test -- --grep "ModelSelector"
# Expected: Test passes verifying filtering logic
✓ filters models by configured provider
✓ shows visual indicator for unconfigured models
✓ updates when provider configuration changes
4. 統合テスト
# Configure a new provider and verify dropdown updates
$ openclaw config add-provider anthropic --api-key sk-ant-...
# Refresh Dashboard
# Verify anthropic models now appear in dropdown
終了コードの検証
# Verify no console errors in Dashboard
$ openclaw dashboard start --debug 2>&1 | grep -i "model\|provider"
[DEBUG] Loading configured providers: ["bailian"]
[DEBUG] Filtering 127 models to 2 available (bailian)
[DEBUG] ModelSelector rendered with 2 options
⚠️ よくある落とし穴
処理が必要なエッジケース
- プロバイダーが一つも設定されていない場合: ドロップダウンには「利用可能なモデルを表示するにはプロバイダーを設定してください」というメッセージとともに空の状態が表示される必要があります
- セッション中にプロバイダーが設定された場合: ModelSelectorはフォーカス時またはウィンドウのフォーカスイベント時にプロバイダーリストを再取得する必要があります
- プロバイダーのAPIキーが無効になった場合: モデルは表示されたままです(サーバー側のバリデーションがランタイムエラーを処理します)
- 初期読み込み時の競合状態: プロバイダー設定の取得中はローディングスケルトンを表示します
環境固有の考慮事項
Dockerデプロイメント
# When running in Docker, provider config is read from:
# 1. Environment variables (PRIORITY)
# 2. Mounted config.yaml at /app/config.yaml
# Verify config is mounted correctly:
$ docker exec <container-id> cat /app/config.yaml | grep -A5 providers
# Expected: Shows configured providers
macOSインストール
# Config location: ~/.openclaw/config.yaml
# Verify file permissions:
$ ls -la ~/.openclaw/config.yaml
-rw-r--r-- openclaw ~/.openclaw/config.yaml
# If permission denied, fix with:
$ chmod 644 ~/.openclaw/config.yaml
Windows (WSL2)
# Config location: ~/.openclaw/config.yaml (WSL2 path)
# Or: C:\Users\<username>\.openclaw\config.yaml
# Ensure line endings are LF, not CRLF:
$ dos2unix ~/.openclaw/config.yaml 2>/dev/null || sed -i 's/\r$//' ~/.openclaw/config.yaml
設定のよくある間違い
# WRONG - provider ID mismatch
providers:
anthropic_api:
type: anthropic # Should match model provider name
CORRECT
providers:
anthropic:
type: anthropic
# WRONG - missing api_key
providers:
anthropic:
type: anthropic
# api_key is required
CORRECT
providers:
anthropic:
type: anthropic
api_key: sk-ant-…
🔗 関連するエラー
関連する問題
- ERR_PROVIDER_NOT_CONFIGURED: 未設定のプロバイダーからのモデルを使用しようとしたときに発生します。現在、UIレベルではなくランタイムに発生します。
- ERR_API_KEY_INVALID: プロバイダーは設定されているが認証情報が無効です。configチェックは通過하지만認証に失敗するモデルのドロップダウン表示に関連します。
- ERR_MODEL_NOT_FOUND: モデルID形式が変更されたがDashboardのモデルリストが古い場合に発生します。OpenClawのバージョンアップグレード後に発生する可能性があります。
過去の経緯
| バージョン | 問題 | 解決策 |
|---|---|---|
| v2026.3.x | プロバイダードロップダウンがconfigに関係なくすべてのプロバイダーを表示 | 部分的な修正 - プロバイダーリストはフィルタリングされたが、モデルリストは未対応 |
| v2026.2.x | API認証情報がconfig.yamlにプレーンテキストで保存されていた | 暗号化のサポートを追加 |
| v2026.1.x | 大きなモデルリストでモデル選択が応答しない | 仮想スクロールを追加 |
関連ドキュメント
デバッグコマンド
# List configured providers
$ openclaw config list --providers
# Test specific provider connectivity
$ openclaw provider test anthropic --model claude-3-5-sonnet
# View supported models for a provider
$ openclaw models list --provider anthropic
# Validate config.yaml
$ openclaw config validate