{"record":{"id":"4082981367269da2","repo":"koala73/worldmonitor","slug":"errormessage","errorCode":null,"errorMessage":"${errorMessage}","messagePattern":"\\$\\{errorMessage\\}","errorType":"http","errorClass":"TelegramLookupError","httpStatus":null,"severity":"error","filePath":"src/services/telegram-intel.ts","lineNumber":138,"sourceCode":"  const header = response.headers.get('retry-after');\n  if (!header) return 0;\n  const seconds = Number(header);\n  return Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : 0;\n}\n\nasync function readJson(response: Response): Promise<unknown> {\n  if (response.ok) {\n    return response.json() as Promise<unknown>;\n  }\n\n  let errorMessage = `${response.status}`;\n  try {\n    const errorJson = await response.json() as { error?: string };\n    errorMessage = errorJson.error || errorMessage;\n  } catch {\n    errorMessage = `${response.status}`;\n  }\n  throw new TelegramLookupError(errorMessage, response.status, parseRetryAfterMs(response));\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n  return value && typeof value === 'object' ? value as Record<string, unknown> : null;\n}\n\nfunction parseTelegramChannelPreview(value: unknown): TelegramChannelPreview {\n  const parsed = asRecord(value);\n  if (!parsed) throw new Error('Invalid Telegram channel preview');\n  const username = normalizeTelegramUsername(String(parsed.username || ''));\n  if (!username) throw new Error('Invalid Telegram channel preview');\n  const memberCount = parsed.memberCount == null ? null : Number(parsed.memberCount);\n  return {\n    username,\n    title: typeof parsed.title === 'string' && parsed.title.trim() ? parsed.title.trim() : username,\n    memberCount: memberCount != null && Number.isFinite(memberCount) && memberCount >= 0\n      ? Math.floor(memberCount)\n      : null,","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/src/services/telegram-intel.ts#L120-L156","documentation":"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.","triggerScenarios":"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: \"...\" }.","commonSituations":"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.","solutions":["Inspect error.status on the caught TelegramLookupError: handle 429 by honoring the parseRetryAfterMs backoff before retrying.","Retry transient statuses (5xx/429) with exponential backoff; the error carries retry-after metadata for this purpose.","Verify the edge Telegram endpoint is deployed and its credentials are configured when the message indicates server-side misconfiguration.","Surface the message to the panel's error/disabled state instead of letting it blank the feed silently."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"function isTelegramLookupError(e: unknown): e is TelegramLookupError {\n  return e instanceof TelegramLookupError;\n}\n","tryCatchPattern":"try {\n  const data = await lookup();\n} catch (e) {\n  if (e instanceof TelegramLookupError) {\n    if (e.status === 429) {\n      await sleep(e.retryAfterMs ?? 5000); // honor Retry-After, then retry\n    } else if (e.status >= 500) {\n      scheduleRetryWithBackoff();\n    } else {\n      showError(e.message); // non-retryable client/server error\n    }\n  }\n}\n","preventionTips":["Respect the Retry-After/backoff metadata carried by TelegramLookupError before reissuing requests","Cache successful lookups (the service does) to reduce upstream pressure","Verify edge deployment and credentials when error bodies indicate server misconfiguration","Keep panel error states distinct from empty states so upstream failures are visible"],"tags":["network","http-error","telegram","api-response"],"backgroundTag":"upstream-api-error-response","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}