koala73/worldmonitor · error · TelegramLookupError

${errorMessage}

Error message

${errorMessage}

What it means

readJson wraps the JSON parse of a Telegram edge lookup/preview response. When the response is not ok, it tries to read an { error } field from the body and throws TelegramLookupError with that server-provided message (falling back to the bare status code), plus the status and any Retry-After hint. The message is whatever the backend sent, so its content varies by endpoint.

Source

Thrown at src/services/telegram-intel.ts:138

  const header = response.headers.get('retry-after');
  if (!header) return 0;
  const seconds = Number(header);
  return Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : 0;
}

async function readJson(response: Response): Promise<unknown> {
  if (response.ok) {
    return response.json() as Promise<unknown>;
  }

  let errorMessage = `${response.status}`;
  try {
    const errorJson = await response.json() as { error?: string };
    errorMessage = errorJson.error || errorMessage;
  } catch {
    errorMessage = `${response.status}`;
  }
  throw new TelegramLookupError(errorMessage, response.status, parseRetryAfterMs(response));
}

function asRecord(value: unknown): Record<string, unknown> | null {
  return value && typeof value === 'object' ? value as Record<string, unknown> : null;
}

function parseTelegramChannelPreview(value: unknown): TelegramChannelPreview {
  const parsed = asRecord(value);
  if (!parsed) throw new Error('Invalid Telegram channel preview');
  const username = normalizeTelegramUsername(String(parsed.username || ''));
  if (!username) throw new Error('Invalid Telegram channel preview');
  const memberCount = parsed.memberCount == null ? null : Number(parsed.memberCount);
  return {
    username,
    title: typeof parsed.title === 'string' && parsed.title.trim() ? parsed.title.trim() : username,
    memberCount: memberCount != null && Number.isFinite(memberCount) && memberCount >= 0
      ? Math.floor(memberCount)
      : null,

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Inspect error.status on the caught TelegramLookupError: handle 429 by honoring the parseRetryAfterMs backoff before retrying.
  2. Retry transient statuses (5xx/429) with exponential backoff; the error carries retry-after metadata for this purpose.
  3. Verify the edge Telegram endpoint is deployed and its credentials are configured when the message indicates server-side misconfiguration.
  4. Surface the message to the panel's error/disabled state instead of letting it blank the feed silently.
Defensive patterns

Strategy: retry

Type guard

function isTelegramLookupError(e: unknown): e is TelegramLookupError {
  return e instanceof TelegramLookupError;
}

Try / catch

try {
  const data = await lookup();
} catch (e) {
  if (e instanceof TelegramLookupError) {
    if (e.status === 429) {
      await sleep(e.retryAfterMs ?? 5000); // honor Retry-After, then retry
    } else if (e.status >= 500) {
      scheduleRetryWithBackoff();
    } else {
      showError(e.message); // non-retryable client/server error
    }
  }
}

Prevention

When it happens

Trigger: Any fetchTelegramChannelPreview/fetchTelegramChannelFeed call where the edge API answers with a non-2xx status: upstream Telegram lookup failure, rate limiting (429), invalid channel handled server-side, or an edge error body containing { error: "..." }.

Common situations: Telegram rate limits or upstream blocks during heavy polling; edge function deployment errors returning JSON error bodies; expired/absent API credentials surfacing as server error messages; network proxies injecting error JSON.

Related errors


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01). Data as JSON: /api/errors/4082981367269da2. Report an issue: GitHub.