El menú desplegable de modelos muestra modelos de proveedores no configurados - Dashboard Model Dropdown Displays Models from Unconfigured Providers
El menú desplegable de selección de modelo del Agente en el Dashboard de OpenClaw muestra todos los modelos compatibles independientemente del estado de configuración del proveedor, lo que causa confusión al usuario al seleccionar modelos no disponibles.
🔍 Síntomas
Comportamiento reportado por el usuario
Al acceder al Dashboard en http://localhost:8080 y navegar a Configuración del Agente, el menú desplegable de Modelo Principal muestra una lista extensa de modelos:
┌─────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────┘
Escenario de fallo
Cuando un usuario selecciona un modelo de un proveedor no configurado (por ejemplo, anthropic/claude-opus-4-5) e intenta guardar o usar el agente:
$ 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.
Manifestación técnica
En la consola del navegador, el componente del menú desplegable renderiza los modelos desde src/models/supported-models.json sin verificar la disponibilidad del proveedor:
[OpenClaw] Model list loaded: 127 models
[OpenClaw] Provider check skipped for dropdown population
🧠 Causa raíz
Problema arquitectónico
El componente selector de modelos del Dashboard (src/components/ModelSelector.tsx) carga la lista completa de modelos soportados desde el registro estático de modelos durante la inicialización del componente:
// 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
}
Verificación de disponibilidad del proveedor faltante
El componente no llama a getConfiguredProviders() o equivalente para filtrar modelos por disponibilidad del proveedor. El registro de modelos y la configuración del proveedor existen en dominios de datos separados sin integración:
┌─────────────────────────────────┐
│ 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 │
└─────────────────────────────────┘
Brecha en el flujo de datos
- El usuario abre el Dashboard → El componente
ModelSelectorse monta - El componente obtiene todos los modelos →
getSupportedModels()devuelve la lista estática completa - Sin llamada API al backend → El componente no consulta
/api/providers/status - El menú desplegable renderiza todos los modelos → Incluyendo los de proveedores no configurados
- El usuario selecciona un modelo inválido → El error de tiempo de ejecución ocurre solo en la ejecución
Ruta del código afectada
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
🛠️ Solución paso a paso
Opción A: Filtrar modelos por proveedores configurados (Recomendado)
Modificar src/components/ModelSelector.tsx para filtrar modelos según la disponibilidad del proveedor:
// 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
}
Opción B: Agregar indicador visual para modelos no configurados
Si se mantiene la visibilidad de todos los modelos con distinción visual:
// 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>
);
}
Opción C: Filtrado controlado por el backend
Agregar un nuevo endpoint de API que devuelva solo los modelos disponibles:
// 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;
🧪 Verificación
Pasos de verificación
Después de aplicar la corrección, verificar el comportamiento a través de estos métodos:
1. Verificación de la interfaz de usuario del Dashboard
# 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. Verificación del endpoint de 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. Prueba unitaria del componente
# 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. Prueba de integración
# 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
Verificación del código de salida
# 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
⚠️ Errores comunes
Casos extremos a manejar
- Sin proveedores configurados: El menú desplegable debe mostrar un estado vacío con el mensaje "Configure un proveedor para ver los modelos disponibles"
- Proveedor configurado durante la sesión: ModelSelector debe volver a obtener la lista de proveedores al recibir el foco o en el evento de foco de la ventana
- La clave API del proveedor se vuelve inválida: Los modelos permanecen visibles (la validación del lado del servidor maneja los errores de tiempo de ejecución)
- Condición de carrera en la carga inicial: Mostrar esqueleto de carga mientras se obtiene la configuración del proveedor
Consideraciones específicas del entorno
Despliegue en 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
Instalación en 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
Errores de configuración
# 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-…
🔗 Errores relacionados
Problemas conectados
- ERR_PROVIDER_NOT_CONFIGURED: Ocurre al intentar usar un modelo de un proveedor no configurado. Actualmente sucede en tiempo de ejecución en lugar de a nivel de interfaz de usuario.
- ERR_API_KEY_INVALID: El proveedor está configurado pero las credenciales son inválidas. Relacionado con el menú desplegable que muestra modelos que pasan la verificación de configuración pero fallan en autenticación.
- ERR_MODEL_NOT_FOUND: El formato del ID del modelo cambió pero la lista de modelos del Dashboard está desactualizada. Puede ocurrir después de una actualización de versión de OpenClaw.
Contexto histórico
| Versión | Problema | Resolución |
|---|---|---|
| v2026.3.x | El menú desplegable de proveedores muestra todos los proveedores independientemente de la configuración | Corrección parcial - la lista de proveedores se filtra, la lista de modelos no |
| v2026.2.x | Las credenciales API se almacenaban en texto plano en config.yaml | Se agregó soporte de cifrado |
| v2026.1.x | El selector de modelos no respondía en listas de modelos grandes | Se agregó desplazamiento virtual |
Documentación relacionada
Comandos de depuración
# 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