Dashboard Model Dropdown Displays Models from Unconfigured Providers
The Agent model selection dropdown in the OpenClaw Dashboard shows all supported models regardless of provider configuration status, causing user confusion when selecting unavailable models.
๐ Symptoms
User-Reported Behavior
When accessing the Dashboard at http://localhost:8080 and navigating to Agent Settings, the Primary Model dropdown displays an extensive list of models:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Failure Scenario
When a user selects a model from an unconfigured provider (e.g., anthropic/claude-opus-4-5) and attempts to save or use the agent:
$ 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.
Technical Manifestation
In the browser console, the dropdown component renders models from src/models/supported-models.json without checking provider availability:
[OpenClaw] Model list loaded: 127 models
[OpenClaw] Provider check skipped for dropdown population
๐ง Root Cause
Architectural Issue
The Dashboard’s model selector component (src/components/ModelSelector.tsx) loads the complete list of supported models from the static models registry during component initialization:
// 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
}
Missing Provider Availability Check
The component does not call getConfiguredProviders() or equivalent to filter models by provider availability. The models registry and provider configuration exist in separate data domains with no integration:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Data Flow Gap
- User opens Dashboard โ
ModelSelectorcomponent mounts - Component fetches all models โ
getSupportedModels()returns complete static list - No API call to backend โ Component does not query
/api/providers/status - Dropdown renders all models โ Including those from unconfigured providers
- User selects invalid model โ Runtime error occurs only at execution time
Affected Code Path
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
๐ ๏ธ Step-by-Step Fix
Option A: Filter Models by Configured Providers (Recommended)
Modify src/components/ModelSelector.tsx to filter models based on provider availability:
// 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: Add Visual Indicator for Unconfigured Models
If maintaining visibility of all models with visual distinction:
// 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: Backend-Driven Filtering
Add a new API endpoint that returns only available models:
// 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;
๐งช Verification
Verification Steps
After applying the fix, verify the behavior through these methods:
1. Dashboard UI Verification
# 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 Endpoint Verification
# 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. Component Unit Test
# 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. Integration Test
# 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
Exit Code Verification
# 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
โ ๏ธ Common Pitfalls
Edge Cases to Handle
- No providers configured: Dropdown should display empty state with message "Configure a provider to see available models"
- Provider configured mid-session: ModelSelector should re-fetch provider list when receiving focus or on window focus event
- Provider API key becomes invalid: Models remain visible (server-side validation handles runtime errors)
- Race condition on initial load: Show loading skeleton while fetching provider configuration
Environment-Specific Considerations
Docker Deployment
# 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 Installation
# 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
Configuration Mistakes
# 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-…
๐ Related Errors
Connected Issues
- ERR_PROVIDER_NOT_CONFIGURED: Occurs when attempting to use a model from an unconfigured provider. Currently happens at runtime rather than UI level.
- ERR_API_KEY_INVALID: Provider is configured but credentials are invalid. Related to the dropdown showing models that pass config check but fail auth.
- ERR_MODEL_NOT_FOUND: Model ID format changed but Dashboard model list is stale. May occur after OpenClaw version upgrade.
Historical Context
| Version | Issue | Resolution |
|---|---|---|
| v2026.3.x | Provider dropdown shows all providers regardless of config | Partial fix - provider list filtered, model list not |
| v2026.2.x | API credentials stored in plain text in config.yaml | Added encryption support |
| v2026.1.x | Model selector unresponsive on large model lists | Virtual scrolling added |
Related Documentation
Debugging Commands
# 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