Telethon Albums Split or Send Twice? Fix Your Channel Relay

A campaign photo set looks like one post in Telegram, but your Telethon relay publishes separate photos—or publishes the grouped set plus the same photos again. Handle grouped media through events.Album and exclude messages with grouped_id from your standalone NewMessage handler. Forward the collected album in one awaited operation.

This guide is for channels you own or are authorized to relay. It covers newly received posts, not a historical archive copier. The example targets Telethon 1.45.0 and Python 3.14.6; verification below is limited to syntax and offline handler tests. No real Telegram messages were sent during preparation.

1. Identify splitting, overlap or an incomplete selection

An album contains several message objects. Their shared grouped_id tells you that they belong together; a single object still represents one item. A NewMessage callback that forwards event.message immediately therefore acts on individual items. A second Album callback can then forward the same set again if both paths are active.

Use a small synthetic post to classify the fault before changing the relay. For example, publish three test photos with a caption in your own source channel and observe the destination. Record which handler ran and the source message IDs without logging private content. These are diagnostic observations, not delivery guarantees.

Observed resultFirst checkCorrect direction
Separate photosPer-item NewMessage forwardingUse Album for grouped media
Album plus separate photosBoth handler paths forwarded grouped itemsSkip grouped items in NewMessage
Only one photo or no expected captionHistory lookup selected one messageRetrieve the intended album members; one ID is insufficient
Whole set appears twiceRepeated registration or multiple workersInspect process ownership and handler installation

2. Install separate handlers once

Call install_relay once with your existing authorized client and two different, resolved chat references. Prefer the marked chat IDs you verified in that account, rather than mixing a username for one reference with an ID for the other. Start and run the client through your existing application entry point; this function does not authenticate or connect.

from telethon import events


def install_relay(client, source, destination):
    if source == destination:
        raise ValueError("Use different source and destination chats")

    async def relay_album(event):
        await event.forward_to(destination)

    async def relay_single(event):
        if event.message.grouped_id is not None:
            return
        await event.forward_to(destination)

    client.add_event_handler(
        relay_album, events.Album(chats=source)
    )
    client.add_event_handler(
        relay_single, events.NewMessage(chats=source)
    )
    return relay_album, relay_single

The Album handler awaits the native forwarding helper for the collected messages. The standalone handler returns before forwarding any grouped item. Both registrations use the same source filter, so posts arriving in the destination do not feed back through this relay. Explicitly keep the source and destination distinct; the simple equality guard cannot recognize two different references to the same chat.

Use this installation as your relay implementation, rather than adding it alongside an older forwarding handler. Repeated installation registers extra callbacks. If a worker already has its handlers installed, restart it through your normal controlled procedure or remove those specific callbacks before reinstalling. Do not launch another process against the same session to experiment; review session ownership first.

Enable your application’s Telethon logging before testing so handler failures remain visible. Avoid a broad except block that turns a rejected forward into an apparent success.

A three-item album reaches Album and NewMessage; grouped items are skipped in NewMessage and forwarded together through Album.
Conceptual event routing. Numbers label illustrative source items; destination message IDs are newly assigned. The filter prevents overlap between these two paths.

3. Preserve forwarding semantics and captions

Use native forwarding when the destination should retain Telegram’s forwarding attribution. Copying media with send_file is a different editorial operation: it creates newly sent content and needs its own caption and formatting handling. Do not replace forwarding with downloading and reuploading when content protection blocks the operation.

For diagnosis, inspect each collected message’s id, grouped_id and whether its text is nonempty. An album’s caption is not guaranteed to live on the particular item you selected. The Album text helper exposes the first captioned item, not a merged transcript of every caption. Forwarding the collected messages avoids reconstructing those captions yourself.

The as_album argument on forward_messages is documented as having no effect in this Telethon release. It cannot collect messages that were never selected. Likewise, iter_messages(limit=1) returns one message, even when Telegram visually groups it with others. Historical retrieval needs separate, explicitly bounded selection and completeness checks; this live-event example does not supply them.

Album events briefly collect related updates before dispatch. That collection helps group newly received media; it does not prove that an offline worker received every historical item. Keep historical recovery separate from the live relay.

4. Check access before treating a failure as an album bug

The logged-in account must be able to receive the source updates and post in the destination. A bot authenticated through Telethon still uses MTProto; it does not gain user-account permissions, and the HTTP Bot API media-group interface is a separate API. Confirm the actual account and chat rights in your test channels.

If Telegram reports protected forwarding, stop that route and ask the owner to choose an allowed publication method. Do not add a copy fallback. If the destination rejects photos or posting, resolve that permission issue before retrying. A number check cannot establish chat access or repair a relay.

If peer resolution fails before the callback can forward, use the input entity guide to resolve the peer in the current account. PSYKE’s Telegram number checks are a separate audience-preparation service; PSYKE does not relay posts, supply customer databases or grant messaging permission.

5. Verify the destination and keep retries deliberate

In your own test channels, try a plain text post, one photo and a three-photo album with an obvious caption. For the album, expect one grouped presentation with three items and the intended attribution. Inspect all items and captions in an official client; a successful function return alone does not establish the visual result.

Next, confirm that the grouped items produced no standalone forwarding calls, and that each registered handler exists only once in one active worker. A restart or uncertain network outcome is a separate test. The grouped_id filter removes overlap between these two callbacks; it is not persistent deduplication across processes, restarts or replayed updates.

For a production relay, record the source chat, source album identity, member IDs, destination and returned destination IDs. If an operation times out after submission, inspect the destination before replaying it. Treat an unknown result as needing reconciliation; do not blindly forward again. Honor server waits and resolve rights errors through the permitted account.

Preparation checks used Telethon 1.45.0 on Python 3.14.6: the Python block was syntax-checked, handler registration used the real event-builder classes, and synthetic events verified standalone forwarding, grouped-item exclusion and one awaited album call. These offline checks do not verify Telegram delivery, caption rendering or permissions. Repeat the small live test in your authorized channels before enabling your relay.

  • One source filter and one installation per worker.
  • Grouped NewMessage items produce no forwarding call.
  • Album forwarding is awaited and exceptions remain visible.
  • Destination item count, captions and attribution are checked.
  • Unknown outcomes are reconciled before any replay.
Explore Telegram number checks

Frequently asked questions

Why do both Album and NewMessage run for one Telegram post?

The album is made of individual messages. Use Album for the grouped operation and return from NewMessage when event.message.grouped_id is not None. This prevents overlap between those handlers, not every possible duplicate.

Can I recover a complete album by fetching its last message?

One message identifies one item. Use its grouped_id to recognize membership, then retrieve and verify the intended members separately. A fixed nearby window can be incomplete; the live Album example is not an archive recovery tool.

Sources and further reading

Related guides