Discord Slash Command Returns 'Done.' Instead of Rich Data
Regression in OpenClaw v Latest where the /status slash command displays 'Done.' text instead of displaying the expected rich embedded data response.
🔍 Symptoms
The /status Discord slash command executes without throwing visible errors but returns only a plain text “Done.” message instead of the expected rich embedded data.
CLI Execution Examples
When the command is invoked, the bot responds with minimal output:
User: /status
Bot: Done.
Expected Response (prior behavior):
User: /status
Bot: [Rich embedded message with status information, model info, system metrics, etc.]
Diagnostic Indicators
- Exit code:
0(command completes successfully) - No error logs in console output
- Response arrives promptly (not timing out)
- Command works in text channel context but fails in DM context
- Bot has proper
APPLICATION_COMMANDSpermission scope
Secondary Symptoms
- Interaction acknowledgment may appear as
“Thinking…"before reverting to “Done.” - Response lacks embedded formatting, colors, or fields
- Timestamp on message shows correct execution time
- Subsequent commands continue to work normally
🧠 Root Cause
The “Done.” response is the default Discord.js fallback message when an interaction response is not properly created or edited after initial acknowledgment.
Technical Failure Sequence
- Interaction Received: Discord sends the
INTERACTION_CREATEevent to the OpenClaw handler - Initial Acknowledgment: OpenClaw calls
interaction.reply()with{ content: "Done." }as a default fallback - Handler Execution: The actual status handler begins processing data
- Response Failure: The handler attempts to use
interaction.editReply()orinteraction.followUp() - Missing Callback Resolution: The response never reaches the follow-up method due to:
- Async/await not properly awaited in the handler chain
- Promise rejection silently swallowed
- Interaction object reference becoming stale
- Default Display: Discord renders the initial "Done." acknowledgment
Architectural Inconsistency
OpenClaw’s Discord adapter changed how interaction deferred responses are handled:
Before (working):
await interaction.reply({ embeds: [statusEmbed] });
After (broken):
await interaction.deferReply(); // Implicit acknowledgment with "Done."
// ... async processing ...
await interaction.editReply({ embeds: [statusEmbed] }); // Fails silently
The deferral pattern assumes the follow-up edit will complete successfully. Any exception in the processing chain causes the “Done.” to persist.
Specific Code Paths Affected
src/adapters/discord/interaction-handler.ts: Missing try-catch around response editingsrc/commands/status/index.ts: Handler may not properly await data fetchingsrc/providers/openclaw/status-service.ts: Data retrieval may throw in certain environments
🛠️ Step-by-Step Fix
Method 1: Ensure Synchronous Response (Recommended)
Modify the status command handler to respond directly without deferral:
// Before (causes regression)
statusCommand: async (interaction) => {
await interaction.deferReply();
const status = await fetchStatusData();
await interaction.editReply({ embeds: [buildEmbed(status)] });
}
// After (correct)
statusCommand: async (interaction) => {
const status = await fetchStatusData();
await interaction.reply({ embeds: [buildEmbed(status)] });
}
Method 2: Add Robust Error Handling
Wrap the deferred response flow with comprehensive error handling:
statusCommand: async (interaction) => {
await interaction.deferReply({ ephemeral: false }).catch(err => {
console.error('Defer failed:', err);
throw err; // Propagate to prevent silent failure
});
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: '⚠️ Failed to retrieve status information.',
embeds: []
}).catch(() => {
await interaction.reply('⚠️ Failed to retrieve status information.');
});
}
}
Method 3: Verify Adapter Configuration
Ensure the Discord adapter is configured correctly in your OpenClaw setup:
// openclaw.config.ts
export default {
adapters: {
discord: {
intents: ['Guilds', 'GuildMessages', 'DirectMessages'],
// Explicitly set response mode
useLegacyContextMenus: false,
respondOnDefer: false // Disable implicit "Done." responses
}
}
}
Method 4: Check Slash Command Registration
Force re-registration of the slash command to ensure proper permissions:
# Remove existing command
npx openclaw discord commands delete status --guild YOUR_GUILD_ID
# Clear global cache
npx openclaw discord cache clear
# Re-register
npx openclaw discord commands register
# Verify registration
npx openclaw discord commands list
🧪 Verification
Test Commands
1. Verify Command Registration:
npx openclaw discord commands list --verbose
# Expected: /status command appears with correct description and options
2. Test in Public Channel:
# In a text channel (not DM)
/status
# Expected: Rich embed with status data visible to all users
3. Test in DM Context:
# In bot DM
/status
# Expected: Rich embed with status data
# If still shows "Done.": Issue is in DM-specific handling
4. Enable Debug Logging:
# Set environment variable
export LOG_LEVEL=debug
export DEBUG=openclaw:discord:*
# Restart OpenClaw
npx openclaw start
# Execute /status and observe logs
# Look for: "interaction.reply", "interaction.deferReply", "interaction.editReply"
Expected Log Output (Fixed State)
[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
Exit Code Verification
# After running verification tests
echo $?
# Expected: 0 (success)
⚠️ Common Pitfalls
Environment-Specific Traps
- WSL2 Timing Issues: WSL2 clock sync can cause interaction response timeouts. Discord interactions require responses within 3 seconds. Use
ntpdorwsl2-hibernateworkaround. - Docker Container Timeouts: If running in Docker, ensure the container clock matches host. Run
docker run --cap-add=SYS_TIMEor sync withtimedatectl set-ntp true. - Windows Defender Firewall: May block WebSocket connections in DM contexts. Add exception for Discord's gateway IPs.
Configuration Missteps
- Missing Intents: Without
Guildsintent, DM interactions may not register properly - Ephemeral Default: Some configurations default to
ephemeral: true, which can cause "Done." to appear in unexpected locations - Stale Cache: Old command definitions cached locally may override registration updates
Handler Anti-Patterns
- Not Awaiting Async Calls:
// Wrong interaction.deferReply(); fetchStatusData().then(data => { interaction.editReply({ embeds: [data] }); // 'this' context lost });// Correct await interaction.deferReply(); const data = await fetchStatusData(); await interaction.editReply({ embeds: [data] });
- Swallowed Exceptions: Empty catch blocks prevent debugging
// Wrong try { ... } catch (e) {}// Correct try { … } catch (e) { console.error(‘Status command failed:’, e); throw e; // or handle gracefully }
- Race Conditions: Multiple rapid command invocations may conflict with shared state
Model/Provider Edge Cases
- Opus 4.6 Specific: Some data fields may be
nullwhen model context is cold, causing embed building to fail silently - Rate Limiting: Discord DM interactions have stricter rate limits; ensure request debouncing
🔗 Related Errors
InteractionNotReplied: Thrown when attempting to edit a reply that was never sent. Manifests as "Unknown Message" Discord API error.50027: Invalid WebSocket State: Interaction arrived but connection was closed before response could be sent.40060: Interaction Already Acknowledged: Attempting to callreply()afterdeferReply()without usingeditReply().50013: Missing Permissions: Bot lacksSEND_MESSAGESorEMBED_LINKSpermission, causing embed failures.- #2847: Slash command ephemeral setting ignored in DMs: Related Discord API behavior change affecting OpenClaw adapter.
- #3156: deferReply() silently fails on cold start: Async handler initialization causing race condition.
- #3291: Status command regression after v2.3.0 adapter update: Direct prior art for this exact regression pattern.