HookEvent.JOB_COMPLETED fires whenever a background subagent reaches a terminal state — whether it succeeded or failed. Register a hook to log completions, send alerts, or trigger follow-up work.
JOB_COMPLETED hook runs when the subagent finishes or fails.
How It Works
Quick Start
How It Works
The user delegates work to a background job; when the job ends, the hook fires with the outcome.JobCompletedInput Fields
JobCompletedInput carries everything about the finished job, including the delivery context if one was set.
| Field | Type | Description |
|---|---|---|
job_id | str | Unique job identifier returned by spawn_subagent(background=True) |
status | str | Terminal state: "completed" or "failed" |
result | Any | The job’s return value on success; None on failure |
error | str | None | Error message on failure; None on success |
deliver | str | Delivery target set at spawn time (e.g. "origin", "telegram:123", or "") |
platform | str | Origin platform captured at spawn time |
chat_id | str | Origin chat/channel ID captured at spawn time |
thread_id | str | Origin thread ID captured at spawn time |
Behaviour Notes
- Fires on both success and failure. Check
data.statusto distinguish. - Best-effort. A raising hook never crashes the background worker — exceptions are caught and logged.
- Sits alongside
SCHEDULE_TRIGGER. Both fire in the background-job lifecycle and follow the same hook shape.
Logging Example
The shortest copy-paste hook that logs every completion to a file:Best Practices
Make hooks idempotent
Make hooks idempotent
Background jobs may be retried or replayed. Design your
JOB_COMPLETED hook so running it twice for the same job_id produces no unintended side effects — for example, check if a log entry already exists before appending, or use an upsert rather than an insert.Do not block the event loop
Do not block the event loop
The hook dispatcher is synchronous by default. Avoid slow I/O (database writes, HTTP calls) inside the hook body — offload to a thread pool or enqueue to a background queue instead:
Errors in hooks are logged, not raised
Errors in hooks are logged, not raised
Per the hook dispatcher in
praisonaiagents/hooks/runner.py, exceptions raised inside a hook are caught and logged — they never crash the background worker. Use this as a safety net, but do not rely on it: log errors explicitly so failures are visible.Chain follow-up work via deliver=, not inline
Chain follow-up work via deliver=, not inline
If a completed job should trigger another agent, use the
deliver field to route the result back through the gateway — do not call a second agent inline from the hook. Inline calls block the dispatcher and can create nested background jobs that are harder to trace.Check status before acting
Check status before acting
Use
data.status to distinguish success from failure. Only trigger alerts or follow-up workflows when status == "failed" to avoid noise from normal completions.Related
Subagent Tool
Spawn background subagents with
deliver= routingHooks
Full hook system reference — all events and patterns

