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.