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.
praisonai serve gateway exits with a specific code to tell your supervisor whether to restart or stop — a misconfigured gateway should not crash-loop forever.
The user runs the gateway under a supervisor; exit codes signal whether to restart, stop for config fixes, or exit cleanly.

Quick Start

1

Start the gateway normally

The process exits with code 0 on clean shutdown, 75 on transient failure (supervisor should restart), and 78 on fatal config error (supervisor should alert, not restart).
2

Systemd service with correct restart policy

3

Kubernetes restart policy

For exit code 78, set an alert in your monitoring system and do not auto-restart.

Exit Code Reference

Exit codes 75 (EX_TEMPFAIL) and 78 (EX_CONFIG) follow the BSD sysexits.h standard, which most Linux supervisors understand natively.

What Triggers Each Code

Fatal configuration (exit 78)

The gateway exits 78 when it detects a problem that restarting won’t fix:
  • Missing required dependencies (e.g. platform SDK not installed)
  • Missing or unreadable gateway.yaml
  • Schema-invalid gateway.yaml (type errors, unknown keys)
  • Unknown / typo’d channel platform — e.g. telegran: when you meant telegram:. Previously warned and started; now fails closed with exit 78.
  • Missing token: on a tokenful platform — the token: key must be present; an empty ${VAR} resolution is tolerated (the channel is skipped), but a missing key is fatal.
  • Plugin platform not on the registry — a platform: name that isn’t built-in, isn’t published via praisonai.channels / praisonai.bots entry points, and isn’t registered at runtime via register_platform() fails closed. To validate against your custom platform, register it before calling WebSocketGateway.start_with_config().
  • --agents file with non-mapping entries
  • FatalConfigError raised by a plugin or adapter during startup

Transient failures (exit 75)

Everything else — network errors, upstream timeouts, unexpected exceptions during operation — exits 75.

Common Patterns

Raise FatalConfigError from a custom plugin

Detect exit code in a shell script

Classify exits programmatically


Supervisor Configuration Reference

systemd

Reload and enable:

How It Works

The classifier (classify_exit_reason) is a pure function with no side effects. It lives in the core package so the wrapper CLI (praisonai serve gateway) and the runtime entry point (python -m praisonai.runtime) share one source of truth.

Exit Code Reference


Fatal vs Transient

classify_exit_reason(exc) applies these rules in order: Clean exit (0):
  • exc is None
  • exc is KeyboardInterrupt (includes SIGTERM mapped to KeyboardInterrupt)
  • exc is SystemExit(0) or SystemExit(None)
  • exc is SystemExit(n) where n is any integer — passes n through unchanged
Fatal (78 — do not restart):
  • FatalConfigError raised explicitly anywhere in the start path
  • gateway.yaml missing, empty, or schema-invalid (load_gateway_config raises ValueError)
  • --agents file missing or unreadable
  • agents: key absent or falsy in the agents YAML
  • agents: is not a list (e.g. agents: {name: bad})
  • agents: list contains a non-mapping entry (e.g. agents: ["bad"]) — previously triggered AttributeError and crash-looped at code 75; now correctly raises FatalConfigError and exits 78
  • Unknown or typo’d channel platform (e.g. telegran:) — previously warned and started; now raises ValueError from the registry-aware validator and exits 78
  • Missing token: key on a tokenful platform — previously warned; now fatal (an empty ${VAR} resolution is tolerated and the channel is skipped)
  • Plugin platform: not on the registry (not built-in, not an entry point, not register_platform()d) — exits 78
  • Missing required gateway dependencies at boot (e.g. pip install praisonai[api] not run)
Transient (75 — ask supervisor to restart):
  • Any Exception not matched by the rules above
Before this fix, a malformed gateway.yaml (missing agents: section, empty file, or schema-invalid YAML) caused load_gateway_config to raise ValueError, which routed through classify_exit_reason to exit 75 — a crash-loop that ran forever. It now exits 78. If your supervisor was relying on the old 75 behaviour to eventually surface the problem, add RestartPreventExitStatus=78 and a matching alert rule.
Before PR #3019, an unknown platform: value only produced a warning and the gateway started with that channel silently dead. It now exits 78. If you have configs with typos that were previously “working” (i.e. reporting healthy while doing nothing), fix them before upgrading.

Supervisor Integration

With --config for multi-bot mode:

Embedding the Classifier

Use classify_exit_reason directly when you run the gateway start path from your own code:
Signal “stop restarting me” from anywhere in a custom start path by raising FatalConfigError:

Backward Compatibility

The wrapper (praisonai serve gateway) imports the exit-code symbols from praisonaiagents.gateway at startup. Two fallback rules apply:
  • If praisonaiagents.gateway is absent (ModuleNotFoundError) or predates the protocol (missing symbols → AttributeError), the wrapper uses local sysexits.h values (0 / 75 / 78) and a minimal classifier with the same semantics.
  • Any other ImportError (a broken core install) surfaces immediately — broken cores are no longer silently swallowed.
Pre-PR #2439 wrappers returned None from start(); the new int return is backward-compatible — callers that ignored the return value continue to work and see exit 0 semantics from the shell.

s6-overlay

Docker Compose

RestartForceExitStatus=75 tells systemd to restart even when Restart=on-failure would not normally trigger (e.g. the process exits quickly). Exit code 78 is NOT in RestartForceExitStatus, so systemd will not restart on fatal config errors.

Kubernetes

Add a liveness probe alert on exit code 78:

s6 / runit


Best Practices

Without it, a typo in gateway.yaml restarts the gateway indefinitely, burning through CPU, log storage, and pager budget.
Exit 75 is normal supervisor noise — the gateway will come back. Exit 78 means a human deployed a broken config and the gateway will never come back on its own.
A 78 at boot is cheap. A 78 caught mid-rollout after rolling out to 10 pods is not.
Catching FatalConfigError and not re-raising converts exit 78 back to 75 and re-enables the crash-loop.
Without this, systemd will restart a misconfigured gateway in a tight crash loop, filling logs and burning CPU. Exit 78 is the standard way to say “don’t restart me”.
If your plugin needs a required API key or a database connection that can’t be deferred, raise FatalConfigError at startup so the operator knows to fix the config before the gateway will run.
A 78 exit in production means a deployment broke the config. Wire it to your on-call alerting so it isn’t silently swallowed by a restart loop.
The gateway exits 0 when it receives a shutdown signal (SIGTERM, /drain endpoint). Supervisors that restart on any non-zero exit will leave the gateway alone after a graceful stop.
Code 78 means the configuration is broken. Auto-restarting on 78 creates a restart loop that burns CPU without making progress. Always alert an operator first.
When restarting on exit code 75, add at least 5 seconds between restarts to avoid hammering a degraded upstream.
At startup, validate that all required API keys and config values are present. Raise FatalConfigError immediately rather than letting the process crash mid-run.
Route stdout/stderr to your log aggregator and alert on exit_code=78 to catch config regressions in CI/CD pipelines.

Gateway Overview

Architecture and startup sequence

Gateway CLI

Command-line interface for the gateway

Gateway Code-Skew Guard

Detect in-place updates and prompt a restart

Gateway Error Handling

How the gateway handles runtime errors