tonhowtf/omniget · warning
[TG] loadMedia failed in ${(performance.now() - t0).toFixed(
Error message
[TG] loadMedia failed in ${(performance.now() - t0).toFixed(0)}ms: What it means
loadMedia() in TelegramBrowser.svelte fetches media items with a Promise.race against a timeout promise; on failure the catch logs '[TG] loadMedia failed in Nms:' with the error message and sets mediaError. The rejection comes either from the underlying fetch/IPC (TDPF/telegram media listing) or from the injected timeout promise, and the elapsed time is logged to help distinguish slow-but-successful calls from hard failures.
Solutions
- Check the logged message and elapsed ms: near-zero ms means an immediate rejection (service/session issue); near the timeout value means a slow fetch.
- Raise the timeout constant or increase PAGE_SIZE tuning if large pages legitimately exceed the deadline.
- Verify the Telegram session/authorization is valid and the media backend service is reachable.
- Add retry with exponential backoff for transient network failures before surfacing mediaError.
- Ensure loadMedia is not re-entered while mediaLoading is true (concurrent calls can race and thrash).
Example fix
// before
const items = await Promise.race([fetchPromise, timeoutPromise]);
// after
let attempt = 0;
const items = await (async () => {
while (attempt < 3) {
try {
return await Promise.race([fetchPromise, timeoutPromise]);
} catch (e) {
if (++attempt >= 3) throw e;
await new Promise((r) => setTimeout(r, 500 * attempt));
}
}
})(); Defensive patterns
Strategy: retry
Validate before calling
// before calling loadMedia
if (mediaLoading) return; // prevent concurrent overlapping fetches
if (!telegramSessionValid) { mediaError = 'Session expired'; return; } Type guard
function isTimeoutError(e: unknown, elapsedMs: number, budgetMs: number): boolean {
if (!(e instanceof Error)) return String(e).includes('timeout');
return elapsedMs >= budgetMs - 50;
} Try / catch
try {
await loadMedia(reset);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (msg.includes('timeout')) {
await retryWithBackoff(() => loadMedia(reset), 3);
} else {
mediaError = msg;
}
} Prevention
- Never race a fetch without a deadline — always pair with a timeout promise like this code does
- Log elapsed time to distinguish slow fetches from hard rejections
- Reduce PAGE_SIZE if large pages routinely hit the timeout
- Debounce infinite-scroll triggers so overlapping loadMedia calls cannot race
- Validate Telegram session/auth before listing media to avoid immediate rejections
When it happens
Trigger: The media fetch promise rejects (backend/IPC error while listing Telegram media) OR the timeout promise fires first because fetching exceeded the configured deadline; the resulting message is then stored in mediaError and rendered in the component.
Common situations: Slow Telegram API/media backend exceeding the page-load timeout on large galleries; network drop or paused backend while scrolling an infinite list; expired Telegram session/authorization causing the media query to reject; media service not running so the invoke fails immediately.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- Download timeout — no data received for 30 seconds
- Timeout downloading segment
- read timed out after {:?}
- unreachable: connect timed out after {:?}
- probe timed out
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/81d4c57d80b74d00.
Report an issue: GitHub.
Appendix: source
Thrown at src/lib/study-components/TelegramBrowser.svelte:1560
offset: reset ? 0 : mediaItems.length,
limit: PAGE_SIZE,
};
if (mediaFilter !== "all") args.mediaType = mediaFilter;
const fetchPromise = telegramListMedia(args);
const timeoutMs = 25_000;
const timeoutPromise = new Promise<TelegramMediaItem[]>((_, reject) =>
setTimeout(
() => reject(new Error(`telegram_list_media timeout (${timeoutMs}ms) — provavelmente FLOOD_WAIT, tente novamente em alguns segundos`)),
timeoutMs,
),
);
const items = await Promise.race([fetchPromise, timeoutPromise]);
console.log(`[TG] loadMedia ok in ${(performance.now() - t0).toFixed(0)}ms, returned ${items.length} items`);
mediaItems = reset ? items : [...mediaItems, ...items];
mediaHasMore = items.length >= PAGE_SIZE;
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
console.warn(`[TG] loadMedia failed in ${(performance.now() - t0).toFixed(0)}ms:`, msg);
mediaError = msg;
} finally {
mediaLoading = false;
}
}
// F0.1 + F0.2: throttle + raf-batch for media thumbs
const thumbFetchLimit = makeLimit(4);
const pendingThumbUpdates = new Map<number, string>();
let thumbFlushScheduled = false;
function flushThumbUpdates() {
thumbFlushScheduled = false;
if (pendingThumbUpdates.size === 0) return;
const next = new Map(mediaThumbs);
for (const [k, v] of pendingThumbUpdates) next.set(k, v);
pendingThumbUpdates.clear();
mediaThumbs = next;
}View on GitHub (pinned to 8600b91f42)