Quick Start
1
It's on by default
The guarantee is always active — you don’t enable it. A normal agent already benefits:
2
Customise the fallback sentence
Set
empty_final_message in BotConfig.metadata to change the sentence shown when a turn produces no visible text:Before this guarantee, three real bugs could each end an inbound turn with no reply: Slack silently dropped a blank send, Telegram parked an empty send in the DLQ, and any adapter could leak a raw
[tool_calls: search_web] placeholder to the user.How It Works
Classification and substitution happen once, atfire_message_sending() — the single delivery funnel every adapter shares — so no adapter re-decides “blank” differently.
A blank, whitespace-only, or [tool_calls: …] final is treated as empty, not silence and substituted with the fallback. A blank reply is never confused with a deliberate NO_REPLY.
classify_final
classify_final is the single decision the bot layer uses to tell a real reply, an empty final, and a deliberate silence apart.
Configuration Options
The fallback sentence is the only knob. Precedence: direct attribute > metadata > default.Operator visibility. Every substitution is logged at Repeated hits usually mean an agent prompt or tool graph is finishing without composing a reply.
INFO on the wrapper logger with the platform name:Best Practices
Keep the fallback short and platform-agnostic
Keep the fallback short and platform-agnostic
The same sentence may ship on Slack, Telegram, Discord, WhatsApp, email, or AgentMail. Avoid platform-specific formatting or references.
Use NO_REPLY when you want silence
Use NO_REPLY when you want silence
To make the bot say nothing on purpose, return the exact
NO_REPLY / [SILENT] token — see Intentional Silence. Returning whitespace will not silence the bot; it still substitutes the fallback.Watch the INFO log for repeated substitutions
Watch the INFO log for repeated substitutions
Frequent
"Empty-final resolution: substituted fallback…" lines signal an agent finishing without a reply. Treat it as a prompt/tool-graph smell, not a delivery problem.Prefer the metadata form in bot.yaml
Prefer the metadata form in bot.yaml
metadata.empty_final_message is the declared seam for platform-agnostic extras and needs no code change — set it in YAML rather than adding a typed knob.Related
Intentional Silence
Deliberate
NO_REPLY / [SILENT] — suppress the send on purposeBot Gateway
Where the visible-outcome guarantee is enforced
Inbound Journal
Crash-replay — the other half of “no inbound ends without a recorded outcome”
Channel Descriptor
Declare per-channel config, prompt hints, and setup in one place
Introduced in PraisonAI PR #3622 (fixes #3621).

