April 22, 2026 • Versión: Latest (unversioned)

[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

  1. Interacción Recibida: Discord envía el evento INTERACTION_CREATE al handler de OpenClaw
  2. Acuse de Recibo Inicial: OpenClaw llama a interaction.reply() con { content: "Done." } como fallback predeterminado
  3. Ejecución del Handler: El handler de estado real comienza a procesar los datos
  4. Falla de Respuesta: El handler intenta usar interaction.editReply() o interaction.followUp()
  5. 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
  6. 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 respuesta
  • src/commands/status/index.ts: El handler puede no hacer await correctamente de la obtención de datos
  • src/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 ntpd o la solución alternativa wsl2-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_TIME o sincronizar con timedatectl 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: true por 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 null cuando 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

Evidencia y fuentes

Esta guía de solución de problemas fue sintetizada automáticamente por la tubería de inteligencia de FixClaw a partir de las discusiones de la comunidad.