Undelivered-Message Notice

Tell the user and alert operators when a gateway reply can't be delivered

When a gateway reply fails permanently, this sends the user a short “couldn’t deliver” notice and fires a MESSAGE_UNDELIVERED hook so operators can route the failure.

Diagram

Undelivered Notice

No

Yes

💬 Reply

Permanent
failure?

🔁 Normal retries

📨 Best-effort notice

🔔 MESSAGE_UNDELIVERED hook

Off by default — existing deployments are unchanged until you opt in.

Quick Start

  1. Turn the notice on

    Set notify_on_undelivered: true in the gateway: block of gateway.yaml.

    yaml
    gateway:
      notify_on_undelivered: true

    Start the gateway as usual — a permanently-undeliverable reply now sends a one-line notice and fires the hook.

    bash
    praisonai gateway start --config gateway.yaml
  2. Customise the notice text

    Add undelivered_template to replace the built-in one-line note.

    yaml
    gateway:
      notify_on_undelivered: true
      undelivered_template: "⚠️ We processed your request but couldn't deliver the reply — please try again."

How It Works

The DeliveryRouter already parks a permanently-failed reply in the DLQ. With the flag on, it also best-effort sends a short notice on the same channel and fires the hook — both fully guarded so they can never mask the original failure.

Diagram
MESSAGE_UNDELIVERED hookChannelDeliveryRouter👤 UserMESSAGE_UNDELIVERED hookChannelDeliveryRouter👤 Userreply parked in DLQalt[notify_on_undelivered]send full replypermanent failurebest-effort one-line notice📨 “couldn’t deliver” notefire(MessageUndeliveredInput)

A large or rich reply may fail while a one-line note still lands, so the notice is attempted even when the original send did not succeed. Whether the note reached the user is reported to the hook as notice_delivered.


Configuration Options

Both keys live under the top-level gateway: block.

KeyTypeDefaultDescription
notify_on_undeliveredboolfalseOpt in to the last-resort user notice and the MESSAGE_UNDELIVERED hook.
undelivered_templatestrbuilt-in noteThe one-line plain-text notice. Kept short so it slips through where the original reply couldn’t.

The MESSAGE_UNDELIVERED Hook

The hook fires the moment a reply is confirmed permanently undeliverable, so an operator can mirror, alert, or re-queue it without patching adapters.

python
from praisonaiagents.hooks import HookRegistry, HookEvent

registry = HookRegistry()

@registry.on(HookEvent.MESSAGE_UNDELIVERED)
def on_undelivered(data):
    # data.platform, data.channel_id, data.content, data.error, data.notice_delivered
    alert_ops(f"Undelivered on {data.platform}:{data.channel_id} — {data.error}")

MessageUndeliveredInput payload fields:

FieldTypeDescription
platformstrChannel platform (e.g. telegram, slack)
contentstrThe reply that could not be delivered (truncated to 500 chars in to_dict)
channel_idstrTarget channel/chat id
errorstrThe delivery error that triggered the failure
notice_deliveredboolWhether the best-effort user notice actually landed

To forward this event to a shell command or HTTP sink with no Python, list message_undelivered under Gateway Outbound Hooks.


Common Patterns

Mirror every undelivered reply to a home channel

python
from praisonaiagents.hooks import HookRegistry, HookEvent

registry = HookRegistry()

@registry.on(HookEvent.MESSAGE_UNDELIVERED)
def mirror(data):
    send_to_ops_channel(
        f"[{data.platform}] to {data.channel_id} failed: {data.error}\n{data.content}"
    )

Notice on, but no operator hook

Set only notify_on_undelivered when you just want the user to hear something and don’t yet handle the hook.

yaml
gateway:
  notify_on_undelivered: true

Best Practices

Keep it off unless silent loss is a problem

The default is OFF and byte-for-byte unchanged. Turn it on for user-facing bots where a silently dropped reply would confuse people.

Keep the template short

The notice exists to slip through where a large/rich reply couldn’t. A one-line plain-text note is far more likely to land than the original.

Use the hook to re-queue, not to retry inline

The reply is already in the DLQ by the time the hook fires. Route it — mirror, alert, or re-queue — rather than assuming the notice guarantees delivery.

Check notice_delivered before you rely on it

notice_delivered tells you whether even the one-line note reached the user. When it’s False, escalate through the operator channel.


Related