Quick Start
1
Simplest — enable via gateway.yaml
2
Custom window
3
Python — build the pure guard
How It Works
The guard runs around the channel supervisor loop. Each crashed boot is recorded; once the trailing window holdsmax_restarts events, the gateway stops resurrecting that channel.
Configuration Options
RestartLoopGuard constructor
Methods:
lifecycle.restart_loop_guard: YAML block
Accepted at the top level of gateway.yaml or nested under gateway:.
Imports
RestartLoopGuard exports from praisonaiagents.gateway. Top-level praisonaiagents does not re-export it.How It Composes With Channel Supervision
The crash-loop guard is a rapid-fire breaker (seconds window) that runs around the supervisor loop. It is orthogonal to the Channel Supervision health-monitor machinery:
Tune all three for a layered defence: the guard catches immediate crash storms per channel;
max_restarts_per_hour catches slow, persistent flapping per channel; the fleet breaker catches a systemic fault that restarts every channel at once.
Log signal
When the guard trips, the gateway emits:Fleet-level breaker
The per-channel guards above catch one bad channel. A systemic fault — a bad shared provider, a network partition, an org-wide expired token — restarts every channel at once, and each one silently burns its ownmax_restarts_per_hour budget in a fleet-wide reconnect storm. FleetSupervisionPolicy sits on top of the per-channel budgets and trips one operator-visible breaker instead.
FleetSupervisionPolicy — a pure aggregate breaker
FleetSupervisionPolicy is a pure, dependency-free, side-effect-free breaker — the aggregate sibling of RestartLoopGuard. Import it from praisonaiagents.gateway:
Recovery
The breaker re-arms cleanly afterbreaker_cooldown_s — once cooldown elapses, tripped() clears the event window so pre-trip events cannot immediately re-trip it. When wired into the gateway, the degraded-owner fact (see below) clears on the next monitor sweep without an external get_status() poll: wait one breaker_cooldown_s after the underlying cause is fixed and the entry disappears on its own.
Observability
When wired into the gateway,get_status() / gateway status / /health gain a fleet block:
The log line on trip:
The
fleet block and the degraded owner flow through health() / gateway status / gateway doctor automatically — WebSocketGateway constructs the supervisor with the shared degraded_registry, so there is nothing to wire. A None registry keeps supervision fully functional (the breaker still holds restarts); it is only silent on the aggregate degraded surface. The standalone single-channel Bot path is unaffected — a 1-channel fleet never false-trips.FleetSupervisionPolicy is YAML-only + direct-Python configuration. It is not an Agent constructor parameter, and you never construct ChannelHealthMonitor yourself — the public Python surface is FleetSupervisionPolicy plus the gateway.health.* YAML keys. Tune the three keys on Channel Supervision › Restart guard-rails.Best Practices
Start with the defaults (3 / 60s)
Start with the defaults (3 / 60s)
The defaults trip after 3 restarts within 60 seconds — a rate that only a genuine crash-on-resume loop hits. Change them only when a channel legitimately flaps (e.g. a flaky upstream) and you want more tolerance.
Combine with max_restarts_per_hour for two-tier defence
Combine with max_restarts_per_hour for two-tier defence
The guard is a fast breaker;
max_restarts_per_hour on Channel Supervision is a slow per-hour cap. Enable both so a rapid crash storm is stopped in seconds and slow persistent flapping is stopped over the hour.After a trip, fix the cause then reconnect
After a trip, fix the cause then reconnect
A trip means the channel stopped auto-resurrecting. Once the underlying crash is fixed, run
praisonai gateway reconnect <channel> (see Channel Supervision → Reconnect) to reset error state and bring the channel back.Tune failing_channel_fraction for the fleet breaker
Tune failing_channel_fraction for the fleet breaker
_count_failing_channels treats a channel as failing when its per-channel restart budget is exhausted (not can_restart). With max_restarts_per_hour = 0 (disabled), idle channels with no recorded restarts are not counted as failing — so a quiet fleet never false-trips the aggregate breaker. Lower failing_channel_fraction only when you want the breaker to fire before half the fleet is down.Wait one cooldown after fixing a fleet-wide fault
Wait one cooldown after fixing a fleet-wide fault
Once you fix the systemic cause (rotate the shared token, heal the partition), the fleet breaker holds restarts for
breaker_cooldown_s then re-arms on its own. The gateway/fleet degraded owner clears on the next monitor sweep — no external gateway status poll required.Related
Channel Supervision
Self-healing channels — the supervisor the guard wraps.
Gateway Exit Codes
Restart-intent exit codes that pair with crash forensics.
Scale to Zero
Sibling lifecycle policy — idle-quiesce for serverless hosts.
Drain Trigger
Sibling lifecycle policy — epoch-safe external drain marker.
Event-Loop Watchdog
Another source of
EX_TEMPFAIL (75) — a wedged asyncio loop.
