[Comando slash de Discord retorna 'Done.' en lugar de datos ricos] - Discord Slash Command Returns 'Done.' Instead of Rich Data
Regresión en OpenClaw v Latest donde el comando slash /status muestra el texto 'Done.' en lugar de mostrar la respuesta esperada de datos incrustados enriquecidos.
🔍 Síntomas
El comando de barra diagonal /status de Discord se ejecuta sin generar errores visibles pero devuelve solo un mensaje de texto plano “Done.” en lugar de los datos enriquecidos esperados.
Ejemplos de Ejecución en CLI
Cuando se invoca el comando, el bot responde con una salida mínima:
User: /status
Bot: Done.
Respuesta Esperada (comportamiento anterior):
User: /status
Bot: [Mensaje enriquecido con información de estado, información del modelo, métricas del sistema, etc.]
Indicadores de Diagnóstico
- Código de salida:
0(comando se completa exitosamente) - Sin registros de error en la salida de consola
- La respuesta llega rápidamente (no hay timeout)
- El comando funciona en contexto de canal de texto pero falla en contexto de DM
- El bot tiene el alcance de permiso correcto de
APPLICATION_COMMANDS
Síntomas Secundarios
- El acuse de recibo de la interacción puede aparecer como
“Thinking…"antes de revertirse a “Done.” - La respuesta carece de formato embebido, colores o campos
- La marca de tiempo en el mensaje muestra la hora de ejecución correcta
- Los comandos posteriores continúan funcionando normalmente
🧠 Causa raíz
La respuesta “Done.” es el mensaje predeterminado de Discord.js cuando una respuesta de interacción no se crea o edita correctamente después del acknowledgement inicial.
Secuencia de Falla Técnica
- Interacción Recibida: Discord envía el evento
INTERACTION_CREATEal handler de OpenClaw - Acuse de Recibo Inicial: OpenClaw llama a
interaction.reply()con{ content: "Done." }como fallback predeterminado - Ejecución del Handler: El handler de estado real comienza a procesar los datos
- Falla de Respuesta: El handler intenta usar
interaction.editReply()ointeraction.followUp() - Resolución de Callback Faltante: La respuesta nunca llega al método de seguimiento debido a:
- Async/await no awaited correctamente en la cadena del handler
- Rechazo de Promise tragado silenciosamente
- La referencia del objeto de interacción se vuelve obsoleta
- Visualización Predeterminada: Discord renderiza el acknowledgement inicial "Done."
Inconsistencia Arquitectónica
El adaptador de Discord de OpenClaw cambió cómo se manejan las respuestas diferidas de interacción:
Antes (funcionando):
await interaction.reply({ embeds: [statusEmbed] });
Después (roto):
await interaction.deferReply(); // Acknowledgement implícito con "Done."
// ... procesamiento async ...
await interaction.editReply({ embeds: [statusEmbed] }); // Falla silenciosamente
El patrón de diferimiento asume que la edición de seguimiento se completará exitosamente. Cualquier excepción en la cadena de procesamiento causa que el “Done.” persista.
Rutas de Código Específicas Afectadas
src/adapters/discord/interaction-handler.ts: Falta try-catch alrededor de la edición de respuestasrc/commands/status/index.ts: El handler puede no hacer await correctamente de la obtención de datossrc/providers/openclaw/status-service.ts: La recuperación de datos puede lanzar excepciones en ciertos entornos
🛠️ Solución paso a paso
Método 1: Asegurar Respuesta Sincrónica (Recomendado)
Modificar el handler del comando status para responder directamente sin diferimiento:
// Antes (causa regresión)
statusCommand: async (interaction) => {
await interaction.deferReply();
const status = await fetchStatusData();
await interaction.editReply({ embeds: [buildEmbed(status)] });
}
// Después (correcto)
statusCommand: async (interaction) => {
const status = await fetchStatusData();
await interaction.reply({ embeds: [buildEmbed(status)] });
}
Método 2: Agregar Manejo de Errores Robusto
Envolver el flujo de respuesta diferida con manejo de errores integral:
statusCommand: async (interaction) => {
await interaction.deferReply({ ephemeral: false }).catch(err => {
console.error('Defer failed:', err);
throw err; // Propagar para evitar falla silenciosa
});
try {
const status = await fetchStatusData();
const embed = buildEmbed(status);
await interaction.editReply({ embeds: [embed] }).catch(err => {
console.error('EditReply failed:', err);
await interaction.reply({ embeds: [embed] }); // Fallback
});
} catch (error) {
console.error('Status fetch failed:', error);
await interaction.editReply({
content: '⚠️ Error al recuperar la información de estado.',
embeds: []
}).catch(() => {
await interaction.reply('⚠️ Error al recuperar la información de estado.');
});
}
}
Método 3: Verificar Configuración del Adaptador
Asegurar que el adaptador de Discord esté configurado correctamente en tu configuración de OpenClaw:
// openclaw.config.ts
export default {
adapters: {
discord: {
intents: ['Guilds', 'GuildMessages', 'DirectMessages'],
// Establecer explícitamente el modo de respuesta
useLegacyContextMenus: false,
respondOnDefer: false // Deshabilitar respuestas implícitas "Done."
}
}
}
Método 4: Verificar Registro del Comando de Barra Diagonal
Forzar el re-registro del comando de barra diagonal para asegurar permisos correctos:
# Eliminar comando existente
npx openclaw discord commands delete status --guild YOUR_GUILD_ID
# Limpiar caché global
npx openclaw discord cache clear
# Re-registrar
npx openclaw discord commands register
# Verificar registro
npx openclaw discord commands list
🧪 Verificación
Comandos de Prueba
1. Verificar Registro del Comando:
npx openclaw discord commands list --verbose
# Esperado: comando /status aparece con descripción y opciones correctas
2. Probar en Canal Público:
# En un canal de texto (no DM)
/status
# Esperado: Embed enriquecido con datos de estado visible para todos los usuarios
3. Probar en Contexto de DM:
# En DM del bot
/status
# Esperado: Embed enriquecido con datos de estado
# Si todavía muestra "Done.": El problema está en el manejo específico de DM
4. Habilitar Logging de Depuración:
# Establecer variable de entorno
export LOG_LEVEL=debug
export DEBUG=openclaw:discord:*
# Reiniciar OpenClaw
npx openclaw start
# Ejecutar /status y observar logs
# Buscar: "interaction.reply", "interaction.deferReply", "interaction.editReply"
Salida de Log Esperada (Estado Corregido)
[DEBUG] openclaw:discord:interaction - Received INTERACTION_CREATE for /status
[DEBUG] openclaw:discord:interaction - Calling status handler
[DEBUG] openclaw:discord:interaction - Fetching status data from provider
[DEBUG] openclaw:discord:interaction - Building embed with 5 fields
[INFO] openclaw:discord:interaction - Replying to interaction with embed
[DEBUG] openclaw:discord:interaction - Response sent: 200 OK
Verificación de Código de Salida
# Después de ejecutar las pruebas de verificación
echo $?
# Esperado: 0 (éxito)
⚠️ Errores comunes
Trampas Específicas del Entorno
- Problemas de Tiempo de WSL2: La sincronización del reloj de WSL2 puede causar timeouts en la respuesta de interacción. Las interacciones de Discord requieren respuestas dentro de 3 segundos. Usa
ntpdo la solución alternativawsl2-hibernate. - Timeouts de Contenedor Docker: Si se ejecuta en Docker, asegurar que el reloj del contenedor coincida con el host. Ejecutar
docker run --cap-add=SYS_TIMEo sincronizar contimedatectl set-ntp true. - Firewall de Windows Defender: Puede bloquear conexiones WebSocket en contextos de DM. Agregar excepción para las IPs del gateway de Discord.
Errores de Configuración
- Intents Faltantes: Sin el intent
Guilds, las interacciones de DM pueden no registrarse correctamente - Predeterminado Efímero: Algunas configuraciones usan
ephemeral: truepor defecto, lo cual puede causar que "Done." aparezca en ubicaciones inesperadas - Caché Obsoleto: Definiciones de comando antiguas cacheadas localmente pueden sobrescribir actualizaciones de registro
Anti-Patrones del Handler
- No Hacer Await de Llamadas Async:
// Incorrecto interaction.deferReply(); fetchStatusData().then(data => { interaction.editReply({ embeds: [data] }); // contexto 'this' perdido });// Correcto await interaction.deferReply(); const data = await fetchStatusData(); await interaction.editReply({ embeds: [data] });
- Excepciones Tragadas: Bloques catch vacíos previenen el debugging
// Incorrecto try { ... } catch (e) {}// Correcto try { … } catch (e) { console.error(‘Status command failed:’, e); throw e; // o manejar elegantemente }
- Condiciones de Carrera: Múltiples invocaciones rápidas del comando pueden entrar en conflicto con estado compartido
Casos Extremos del Modelo/Proveedor
- Opus 4.6 Específico: Algunos campos de datos pueden ser
nullcuando el contexto del modelo está frío, causando que la construcción del embed falle silenciosamente - Rate Limiting: Las interacciones de DM de Discord tienen límites de tasa más estrictos; asegurar debouncing de solicitudes
🔗 Errores relacionados
InteractionNotReplied: Se lanza cuando se intenta editar una respuesta que nunca fue enviada. Se manifiesta como error de API de Discord "Unknown Message".50027: Invalid WebSocket State: La interacción llegó pero la conexión se cerró antes de que la respuesta pudiera ser enviada.40060: Interaction Already Acknowledged: Intentar llamar areply()después dedeferReply()sin usareditReply().50013: Missing Permissions: El bot carece del permisoSEND_MESSAGESoEMBED_LINKS, causando fallas en el embed.- #2847: Configuración efímera del comando de barra diagonal ignorada en DMs: Cambio de comportamiento relacionado de la API de Discord que afecta al adaptador de OpenClaw.
- #3156: deferReply() falla silenciosamente en arranque en frío: Inicialización del handler async causando condición de carrera.
- #3291: Regresión del comando status después de actualización del adaptador v2.3.0: Antecedente directo para este patrón exacto de regresión.