Skip to main content
The dead-target registry stops your gateway from wasting requests on channels where the bot was kicked, the group was deleted, or a 403/404 is permanent — and automatically self-heals when a target recovers. The user triggers an outbound send; the registry skips dead targets and re-probes them when recovery is due.
The registry is default OFF. The DeliveryRouter works exactly as before until you construct and attach a DeadTargetRegistry instance.

Quick Start

1

Enable the Registry

Attach a registry to suppress known-dead targets:
2

Persist to a Custom Path

By default the registry persists to ~/.praisonai/state/dead_targets.json. Override it:
3

Share Across Multiple Workers

For a horizontally-scaled gateway, pass a shared 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.
Every registry sharing a store must use identical ttl_seconds and max_size. The dead-target sweep is table-wide (no per-registry column), so divergent bounds would silently prune the other registry’s suppressions. DeliveryControlStore.register_dead_config() enforces this at construction: a second registry attaching to the same store with different bounds raises ValueError with a clear message. Use separate store files for registries with different settings.
4

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

Platform names are stored lower-cased. 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’s send_message() can signal delivery outcome three ways:
The 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.
Dead channels are skipped silently; recovered channels re-enter service within one reprobe cycle (~1 h by default).
On a multi-worker gateway, pass the same 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

Attach a registry only on long-running gateways with periodic, broadcast, or scheduled sends. Single-request CLIs and request-response bots don’t need it — adding suppression to short-lived processes just adds complexity with no benefit.
When several workers share one 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.
Never treat HTTP 401 Unauthorized as a dead-target signal. Token expiry would suppress every channel simultaneously, and the 30-day TTL would hide the fix for a month. Auth failures stay on the normal error path so the operator sees and fixes them quickly.
Store the registry on a persistent local path — not /tmp and not a container tmpfs. The default ~/.praisonai/state/dead_targets.json is the right location; use it as a template.
The default 1-hour reprobe window suits most periodic-update gateways. Raise it if kicked-bot scenarios are rare and you’d rather not probe recovered channels frequently. Lower it if recovery latency matters (e.g. high-priority alert channels).
Expose 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.
The registry is most valuable when you push messages to many stored targets (newsletters, alerts). For one-on-one chat bots, the existing retry path is sufficient.
The JSON file survives restarts and preserves which channels are suppressed between runs. Store it on the same persistent volume as your SQLite approval store.
When a successful send reaches a channel that was in the dead list — even a regular delivery, not only a scheduled re-probe — the channel is cleared automatically. No manual intervention needed.
The 30-day default suits most bots. On platforms where users frequently delete and recreate accounts, use a shorter TTL (e.g. 1–7 days) to allow re-delivery sooner.
Each entry uses ~100 bytes. max_size=10_000 uses ~1 MB. For bots with millions of users, increase max_size proportionally or use a persistent backend.
If an operator manually restores a blocked channel, call registry.clear(platform, channel_id) so the next send goes through. Without clearing, the entry persists until TTL expiry.
The registry prevents re-sending to dead targets, but in-flight messages that fail permanently should also go to the outbound DLQ. See Durable Delivery and Delivery Config.
If users typically re-add your bot within 7 days, set ttl_seconds=604800. The registry will forget the entry and perform a clean probe attempt rather than waiting the full 30-day default.
A rapidly-growing registry is a signal that your bot is being kicked at scale. Investigate the root cause before the registry fills to max_size and starts evicting entries.
The registry writes atomically to a JSON file. On failure the write is logged as a warning but never raised — the in-memory state is still correct and the file will be updated on the next successful write.

Durable Delivery

Persistent outbox + retry for transient failures — composes with the dead-target registry

Channel Supervision

Self-healing gateway channels with operator pause/resume/reconnect controls

Proactive Delivery

Scheduled and broadcast outbound message delivery

Delivery Config

Delivery surface configuration reference