Skip to content

[Bug] Telegram media group: silent image loss when one photo fails to download #55216

Description

@Round-Polar-Bear

Summary

When sending multiple images as a Telegram media group (album), if one photo fails to download (network error, API timeout, file too large, etc.), it is silently dropped with no notification to the user. The AI receives fewer images than were sent, with no indication of what happened.

Version: 2026.3.23-2
Channel: Telegram

Reproduction

  1. Send 8 images as an album (media group) via Telegram
  2. One photo encounters a download error (e.g., network blip, 30s read idle timeout on a large file)
  3. AI responds as if only 7 images were received — user is never told one was lost
  4. In some cases the failed image produces a partial/corrupted file ("fragment") rather than being absent entirely

Root Cause

1. Silent skip on download failure (pi-embedded-CbCYZxIb.js, processMediaGroup)

try {
    media = await resolveMedia(item.ctx, mediaMaxBytes, opts.token, telegramTransport, telegramCfg.apiRoot);
} catch (mediaErr) {
    if (!isRecoverableMediaGroupError(mediaErr)) throw mediaErr;
    runtime.log?.(warn(`media group: skipping photo that failed to fetch: ${String(mediaErr)}`));
    continue; // ← silently skipped, user never notified
}
if (media) allMedia.push(...); // ← null also silently dropped

When resolveMedia throws a recoverable error (MediaFetchError, MediaSizeLimitError) or returns null (after getFile() exhausts 3 retries), the photo is just skipped. No error reply is sent to the user.

2. Partial download ("fragment") from idle timeout

downloadAndSaveTelegramFile uses a 30-second read idle timeout (TELEGRAM_DOWNLOAD_IDLE_TIMEOUT_MS = 30000). A large image that stalls mid-download can produce an incomplete file that gets passed to the AI as valid media.

3. Timer race condition (minor)

The media group buffer uses a 500ms flush timer (mediaGroupTimeoutMs = 500). Each incoming photo resets the timer. On slow connections with 8+ photos, the last photo(s) could theoretically arrive after the timer fires, causing them to be processed as a separate (incomplete) group. This is testTimings.mediaGroupFlushMs internally but is not exposed as a user-configurable option.

Expected Behavior

When one or more photos in a media group fail to download, the user should receive an error reply indicating which images could not be processed, e.g.:

"Received 7 of 8 images — 1 photo could not be fetched (download failed). Please resend the missing image."

Suggested Fix

In processMediaGroup, track failed photo count and send a follow-up error reply after processing:

let failedCount = 0;
for (const item of entry.messages) {
    try {
        media = await resolveMedia(...);
    } catch (mediaErr) {
        if (!isRecoverableMediaGroupError(mediaErr)) throw mediaErr;
        runtime.log?.(warn(`media group: skipping photo...`));
        failedCount++;
        continue;
    }
    if (media) allMedia.push(...);
    else failedCount++;
}

if (failedCount > 0) {
    // send user-visible warning reply
    await sendReply(primaryEntry.ctx, `⚠️ ${failedCount} of ${entry.messages.length} images could not be fetched and were skipped.`);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions