DeliveryRouter works exactly as before until you construct and attach a DeadTargetRegistry instance.Quick Start
Enable the Registry
Persist to a Custom Path
~/.praisonai/state/dead_targets.json. Override it:Share Across Multiple Workers
DeliveryControlStore so every worker sees the same dead-target set. Marking a channel dead in worker A immediately suppresses sends from worker B; a successful send from any worker self-heals across all of them.Inspect and Clear Manually
How It Works
Self-heal timeline:Configuration Options
DeadTargetRegistry constructor
Public methods
DeadTarget is a frozen dataclass: platform: str, channel_id: str, reason: str, ts: float (Unix epoch).
What Counts as Permanent
is_permanent_target_failure(err, platform) classifies an exception before DeliveryRouter calls mark_dead.
Permanent — target marked dead
Not permanent — stays on the retry path
mark_dead("Telegram", ...) and is_dead("TELEGRAM", ...) refer to the same entry. Users coming from YAML keys like Telegram: don’t need to worry about casing.Adapter Return Conventions
A channel adapter’ssend_message() can signal delivery outcome three ways:
False convention was formalised in PraisonAI #2578. Adapters that previously returned False silently to signal “send skipped” should now raise an exception or return None to avoid being misclassified as a transient failure.Common Patterns
Long-running broadcast gateway
A gateway pushing periodic updates to many channels suppresses dead ones so they don’t burn rate-limit budget on every cycle.DeliveryControlStore to every worker’s registry and rate limiter. That way, a channel marked dead by worker A immediately stops burning fan-out cycles on workers B/C/D. See Multi-worker (horizontally-scaled gateway).Operator dashboard via list_dead()
Expose suppressed channels in a health or admin endpoint so operators can see what has been suppressed and why.
Custom suppression for one chat
Un-suppress a channel manually after confirming the bot has been re-added.Best Practices
Default-OFF on purpose
Default-OFF on purpose
Keep bounds consistent across workers
Keep bounds consistent across workers
DeliveryControlStore, they must agree on ttl_seconds and max_size. DeliveryControlStore.register_dead_config() enforces this at attach time — the second worker with divergent bounds raises ValueError instead of silently pruning the first worker’s suppressions. Roll config changes through the whole fleet together; if you genuinely need two different retention windows, use two separate store files.401 is intentionally NOT permanent
401 is intentionally NOT permanent
Keep persist_path on a real filesystem
Keep persist_path on a real filesystem
/tmp and not a container tmpfs. The default ~/.praisonai/state/dead_targets.json is the right location; use it as a template.Tune reprobe_seconds to your fan-out cadence
Tune reprobe_seconds to your fan-out cadence
list_dead() is your operator dashboard
list_dead() is your operator dashboard
list_dead() in your gateway’s admin or health endpoint so operators can see suppressed channels at a glance. Pair it with registry.size() for a quick health metric.Use for broadcast / proactive delivery bots
Use for broadcast / proactive delivery bots
Store on a durable volume
Store on a durable volume
Self-healing is automatic
Self-healing is automatic
Keep TTL short for fast-recovering platforms
Keep TTL short for fast-recovering platforms
Size the registry to your user base
Size the registry to your user base
max_size=10_000 uses ~1 MB. For bots with millions of users, increase max_size proportionally or use a persistent backend.Always clear on manual recovery
Always clear on manual recovery
registry.clear(platform, channel_id) so the next send goes through. Without clearing, the entry persists until TTL expiry.Pair with durable delivery
Pair with durable delivery
Set TTL to match your re-subscription window
Set TTL to match your re-subscription window
ttl_seconds=604800. The registry will forget the entry and perform a clean probe attempt rather than waiting the full 30-day default.Monitor the registry size
Monitor the registry size
max_size and starts evicting entries.Persistence is best-effort
Persistence is best-effort

