agentscope-ai/agentscope · error · ChannelError

Channel '{channel_id}' not found.

Error message

Channel '{channel_id}' not found.

What it means

Thrown by ChannelService._require when storage.get_channel returns None for the given channel_id; it is the shared guard used by update and delete. It signals the channel id does not exist (never created or already removed) and surfaces as ChannelError with HTTP 404.

Source

Thrown at src/agentscope/app/_service/_channel.py:209

        fields = await self._bus.registry_getall(
            MessageBusKeys.channel_seen_chats(channel_id),
        )
        return sorted(fields.keys())

    # -- internals --

    async def _require(self, channel_id: str) -> ChannelRecord:
        """Load a channel record or raise a 404 ``ChannelError``.

        Args:
            channel_id (`str`): The channel to load.

        Returns:
            `ChannelRecord`: The record.
        """
        record = await self._storage.get_channel(channel_id)
        if record is None:
            raise ChannelError(f"Channel '{channel_id}' not found.", 404)
        return record

    async def _notify(self, channel_id: str) -> None:
        """Publish a lifecycle notification (best-effort).

        Args:
            channel_id (`str`): The changed channel; reconcile re-reads
                storage, so the payload is only a nudge.
        """
        try:
            await self._bus.publish(
                MessageBusKeys.channel_lifecycle(),
                {"channel_id": channel_id},
            )
        except Exception:  # pylint: disable=broad-except
            # Lost notifications are recovered by the periodic reconcile.
            pass

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Verify the channel still exists (list channels) before update/delete
  2. Treat 404 ChannelError in delete flows as already-deleted success when idempotency matters
  3. Refresh channel ids from the current environment instead of hardcoding them in configs
  4. Guard against double-submits in the UI by disabling the button after the first request

Example fix

# before
await channel_service.delete(channel_id="ch-old")  # 404
# after
from ... import ChannelError
try:
    await channel_service.delete(channel_id="ch-old")
except ChannelError as e:
    if e.status_code != 404:
        raise  # already gone; treat as success
Defensive patterns

Strategy: try-catch

Validate before calling

record = await channel_client.get_channel(channel_id)
if record is None:
    raise ValueError(f"Channel {channel_id} does not exist")
await channel_client.update(channel_id, ...)

Try / catch

try:
    await channel_service.delete(channel_id)
except ChannelError as e:
    if e.status_code == 404:
        return  # already deleted; idempotent success
    raise

Prevention

When it happens

Trigger: Calling update(channel_id, ...) or delete(channel_id) with an id that was never registered, was deleted earlier, or came from a stale config/UI list.

Common situations: Deleting a channel twice (double click or retry after the first request succeeded); config files or environment variables holding a channel id from a previous environment; races where another admin removed the channel concurrently.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/e354a6375bea0f14. Report an issue: GitHub.