Skip to main content
The gateway now ships in the praisonai-bot package. praisonai serve gateway still works exactly as documented here; for a standalone install see praisonai-bot Migration.
Channel supervision keeps gateway bots alive through network outages with unlimited retries and operator-level pause / resume / reconnect controls. Channels with bad or expired tokens never reach the supervisor — praisonai gateway start --preflight (default on) aborts before launch when a credential probe fails; see Pre-flight credential check. Similarly, praisonai gateway start --strict-tools (default on) aborts before launch when any tool named in the config cannot be resolved; see Pre-flight tool check.
The user messages the bot on Telegram; channel supervision reconnects the channel after network blips without operator intervention.

Quick Start

Channel supervision is automatically enabled for all gateway channels configured in gateway.yaml. No additional setup is required.
1

Basic Gateway Setup

Create a simple gateway with supervision:
The telegram channel is now under supervision with unlimited retry capability.
2

Control Channel Operations

Pause a problematic channel while investigating issues:
Resume when ready:
Force reconnect to reset error state:

How It Works

Channel supervision provides resilient error handling through error classification and unlimited retries:

Channel States

The supervision system tracks five distinct channel states:
Proactive health monitoring can move a channel from RUNNING into a restart cycle without a raised exception — for example when transport activity goes stale (stale-socket) or a probe fails (disconnected).

Recovery-triggered outbox re-drain

When a channel transitions back to RUNNING after a recoverable failure (monitor.attempt > 0), the supervisor schedules a background re-drain of the durable outbox so held replies go out promptly.
  • Non-blocking — scheduled with asyncio.create_task(...), so supervision never waits on the re-drain.
  • Silent no-op without a hook — the supervisor resolves drain_outbox on the supervised object first, then on bot.adapter. Bots/adapters without the hook are skipped.
  • Best-effort — a failed re-drain is logged at WARNING and swallowed; the outbox’s attempt-and-age policy still governs those messages.
  • Recovery only — a clean first start does not trigger it; only a real recovery (at least one recoverable failure) does.
_on_channel_recovered is an internal ChannelSupervisor method — this behaviour is automatic. There is no new config knob, YAML key, or export to wire up.
Cross-references: Durable Delivery → When the Outbox Drains (all three drain triggers) and Outbound Resilience → Automatic crash-recovery on start (the separate boot-time DLQ drainer). Introduced in PraisonAI PR #4046 (fixes #4043).

Proactive Health Monitoring

The health monitor periodically asks each channel “Are you really alive?” and restarts the ones that aren’t, without waiting for an exception to be raised.

Enable via YAML

Enable via Python

Configuration Options

HealthMonitorConfig.from_dict defensively parses the three fleet keys — a bad value clamps or falls back to the default instead of raising, matching the other numeric keys.

HealthReason values

Decision order in evaluate_channel_health():

Passive inbound liveness

Channel liveness is driven by whether messages are still flowing IN, not just whether the outbound probe (e.g. Telegram getMe) succeeds. Every inbound message refreshes the timestamp via fire_message_received → _note_inbound(), so a reachable-but-deaf channel will eventually trip stale-socket instead of being reported healthy forever. All adapters get this automatically; WhatsApp and Linear are wired explicitly because they bypass the shared handler.

Run awareness

The evaluator knows about in-flight agent turns (HealthResult.active_runs) and tracks per-run progress (HealthResult.last_run_progress, refreshed on tool calls, streamed tokens, and agent emitter events). A busy channel is never killed mid-run — BUSY is non-recoverable on purpose. Only when a busy channel has made no progress at all (neither inbound transport activity nor in-run progress) for stuck_after seconds does it escalate to STUCK (recoverable). An actively-streaming long agent turn stays BUSY indefinitely; a genuinely-hung turn that emits nothing for stuck_after is still correctly flagged STUCK.
Before praisonaiagents 1.6.82, only inbound transport activity counted toward progress, so a single long agent turn (deep research, big refactor, slow model) was classified STUCK once it crossed stuck_after — even while actively streaming. The protocol now honours in-run progress (HealthResult.last_run_progress), so progressing long runs stay BUSY indefinitely. See PraisonAI #2400.

Restart guard-rails

  • Startup grace — no restarts during the first startup_grace seconds after connect
  • 5-minute cooldown — implicit cooldown after every restart (logged as restart cooldown active)
  • max_restarts_per_hour — when the cap is hit, a warning is logged and the restart is skipped
  • Crash-Loop Guard — a rapid-fire breaker (seconds window) that runs around the supervisor loop and halts auto-resume after a burst of crash-on-resume restarts, complementing the per-hour cap
  • Fleet-level breaker — an aggregate breaker (fleet_restarts_per_hour, failing_channel_fraction, breaker_cooldown_s) that trips once when a systemic fault restarts every channel at the same time, instead of each channel silently burning its own budget

Fleet breaker status

get_status() (surfaced by gateway status and /health) includes a fleet block so operators can see the aggregate breaker at a glance:
breaker_tripped: true means restarts are being held fleet-wide; the same event records one gateway/fleet entry on the Degraded-Capability Registry. See Fleet-level breaker for the full model.

Suspend / resume monitoring

For planned maintenance, suspend health checks on a channel without stopping supervision:

Operator Controls

Pause Channel

Temporarily stop a channel without losing configuration:
Effect: Channel enters PAUSED state and stops processing messages. Supervision loop waits indefinitely until resumed.

Resume Channel

Resume a manually paused channel:
Effect: Channel transitions from PAUSED to STOPPED, then automatically restarts to RUNNING.

Reconnect Channel

Force a complete reconnection and reset error state:
Effect: Resets retry counter, clears error history, forces restart. Works from any state including FAILED.

Error Classification

The supervision system classifies errors to determine retry behavior: The retry policy uses capped exponential backoff:
  • Initial delay: 5 seconds
  • Maximum delay: 300 seconds (5 minutes)
  • Unlimited attempts for recoverable errors
  • Jitter added to prevent thundering herd

Runtime credential rejection

A mid-flight 401/403 or revoked-token error is its own outcome — the channel parks in CREDENTIAL_UNAVAILABLE, not FAILED. FAILED is terminal and assumes a full process restart is needed. A credential rejection is different: it self-heals the moment the operator fixes the token, so the supervisor stops hammering the invalid token and waits — it does not loop-retry — until a reconnect(), resume(), or config hot-reload wakes it, then restarts the same bot instance without a process restart. This is the runtime counterpart of the boot-time case documented on Degraded Channel Isolation: boot-time = an empty token at config load; runtime = a valid-looking token the platform rejected while the channel was live. Both surface identically as status: "degraded", reason: "credential unavailable".
last_error is always the literal string "credential unavailable" — never the token, never the raw exception message. Operator dashboards can display last_error verbatim with no risk of leaking a secret.

Recovering from CREDENTIAL_UNAVAILABLE

1

Fix the credential

Rotate the env var, refresh the mounted secret file, or re-issue the OAuth token so a fresh, valid credential is available.
2

Trigger recovery

Any of three paths wakes the parked channel:
  • Config hot-reload — rebuilds the bot with the new token; the reload’s abort signal wakes the parked channel. See Gateway Hot Reload.
  • praisonai gateway reconnect <channel> — enough when the token lives in an env var (base Bot.start() re-reads env-var tokens on restart) or when the bot implements refresh_credentials().
  • praisonai gateway resume <channel> — only if you had also paused it; the manual_pause flag is orthogonal to the credential state.
3

Verify recovery

Check GET /health: the channel’s status returns to running and it drops out of any degraded view.

Implementing refresh_credentials() on custom bots

A bot can opt in to re-sourcing its token on wake by exposing a refresh_credentials() method — the only new API surface bot authors need for this feature.
  • Duck-typed — subclassing Bot is optional; any object with a callable refresh_credentials attribute works.
  • Sync or async — the supervisor awaits coroutines.
  • Best-effort — a raising hook logs a warning and the restart proceeds anyway; if the credential is still bad the channel simply re-parks, so there is no infinite loop.
  • When you need it — only when your credential lives outside env vars / mounted files and cannot be picked up by a hot-reload or by Bot.start() re-reading env vars. Most operators do not need this.

Monitoring via /health

The enhanced health endpoint includes supervision status for each channel:
Key supervision fields:
  • state: Current channel state (running, failed, paused, stopped, credential-unavailable)
  • last_error: Most recent error message (if any)
  • last_error_time: Unix timestamp of last error
  • next_retry_at: Unix timestamp of next retry attempt (if scheduled)
  • total_recoveries: Count of successful recoveries from errors
  • manual_pause: Whether channel is manually paused by operator
  • active_runs: Number of in-flight agent turns (busy count)
  • last_activity: Unix timestamp of last inbound transport activity (used for stale-socket and stuck detection)
  • health_monitor.enabled: Whether proactive monitoring is active
  • health_monitor.restart_count: Restarts in the current hour
  • health_monitor.can_restart: Whether guard-rails allow another restart

Best Practices

Use pause for temporary investigations while keeping the channel configuration intact. Use reconnect when you need to reset error state after fixing underlying issues like network connectivity or API tokens.
High total_recoveries counts indicate frequent connection issues. Monitor this metric to identify unstable network conditions or platform-specific problems that may require infrastructure changes.
The /health endpoint is designed for integration with Prometheus, Datadog, or other monitoring systems. Set up alerts on state: "failed" and track total_recoveries trends to detect degrading connection quality. Prefer alerting on the status: "degraded" health-endpoint projection for credential issues — it unifies boot-time and runtime credential unavailable cases in one check.
Channels in FAILED state require manual intervention. Use reconnect (not resume) to reset the error state and attempt a fresh connection. Always investigate the last_error to address root cause issues before reconnecting.An auth/credential rejection at runtime now goes to CREDENTIAL_UNAVAILABLE (self-healing), not FAILED. If you already alert on state: "failed" for token expiry, add state: "credential-unavailable" to that alert — or, recommended, alert on the status: "degraded" health-endpoint projection instead, which unifies the boot-time and runtime cases.
Low-traffic channels need a larger stale_after (e.g. 600s) to avoid false stale-socket restarts. Chatty channels can use the default 120s.
If your agent regularly runs turns longer than 15 minutes (deep research, long tool chains), raise stuck_after so genuine progress isn’t classified as wedged. The default 900s covers most chat and triage workloads.
When the upstream API is rate-limited, restart storms make the problem worse. Lower the cap (e.g. 3) so the guard-rail surfaces the issue in logs instead of hammering the API.

Degraded Channel Isolation

Boot-time credential-unavailable channels — the same degraded surface as runtime rejection

Dead Target Registry

Suppress permanently-dead channels — bot kicked, chat deleted, account deactivated

Send Error Taxonomy

Structured SendErrorKind classification behind auth-fatal and permanent-target outcomes

Gateway CLI

Complete CLI reference for gateway management

Gateway Error Handling

Error handling strategies for gateway bots

BotOS

Multi-platform orchestrator with the same supervision and health monitoring

Bot Loop Protection

Break runaway bot-to-bot reply loops — a pure decision protocol like evaluate_channel_health