{"record":{"id":"1b6929f43ec39aae","repo":"hcengineering/platform","slug":"failed-to-fetch-photo","errorCode":null,"errorMessage":"Failed to fetch photo","messagePattern":"Failed to fetch photo","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"services/telegram-bot/pod-telegram-bot/src/server.ts","lineNumber":172,"sourceCode":"    '/photo/:fileId',\n    wrapRequest(async (req, res) => {\n      const { fileId } = req.params\n      const fileLink = await bot.telegram.getFileLink(fileId)\n\n      const response = await fetch(fileLink.toString())\n      if (!response.ok) {\n        res.status(response.status).send(response.statusText)\n        return\n      }\n\n      if (response.body != null) {\n        res.setHeader('Content-Type', response.headers.get('Content-Type') ?? 'application/octet-stream')\n        res.setHeader('Content-Length', response.headers.get('Content-Length') ?? '0')\n\n        const stream = Readable.fromWeb(response.body as ReadableStream<any>)\n        stream.pipe(res)\n      } else {\n        res.status(500).send('Failed to fetch photo')\n      }\n    })\n  )\n\n  app.use((err: any, _req: any, res: any, _next: any) => {\n    if (err instanceof ApiError) {\n      res.status(err.code).send({ code: err.code, message: err.message })\n      return\n    }\n\n    res.status(500).send(err.message?.length > 0 ? { message: err.message } : err)\n  })\n\n  return app\n}\n\nexport function listen (e: Express, ctx: MeasureContext, port: number, host?: string): Server {\n  const cb = (): void => {","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/telegram-bot/pod-telegram-bot/src/server.ts#L154-L190","documentation":"The telegram-bot photo endpoint fetches a file from the Telegram Bot API (getFile/download) and pipes it to the response. When the upstream fetch response is not OK, the handler sends HTTP 500 with the plain text 'Failed to fetch photo' instead of streaming the image.","triggerScenarios":"A client requests the photo route while the fetch to Telegram's file API returns a non-OK status — expired/invalid bot token, invalid file_id, Telegram network failure, or file too large to download via the Bot API.","commonSituations":"Telegram file_path expiring (file links are valid for ~1 hour), BOT_TOKEN misconfigured in the bot pod environment, avatar/photo id from an old message whose file is no longer resolvable, or network egress from the pod to api.telegram.org blocked.","solutions":["Verify the BOT_TOKEN environment variable is valid and the bot can call getMe.","Check that the file_id/path parameter passed to the Telegram getFile call is current — re-resolve it via getFile before downloading.","Test outbound connectivity from the pod to api.telegram.org (DNS/proxy/firewall).","Add logging of the upstream response.status in the else branch to distinguish 401 (token) from 400 (bad file_id) and retry once with a fresh getFile."],"exampleFix":"// before\n} else {\n  res.status(500).send('Failed to fetch photo')\n}\n// after\n} else {\n  console.error('photo fetch failed', response.status)\n  if (response.status === 404) { /* re-getFile and retry once */ }\n  res.status(502).send(`Failed to fetch photo: upstream ${response.status}`)\n}","handlingStrategy":"retry","validationCode":"async function canReachTelegram(): Promise<boolean> {\n  const r = await fetch(`https://api.telegram.org/bot${process.env.BOT_TOKEN}/getMe`)\n  return r.ok\n}\nif (!(await canReachTelegram())) throw new Error('BOT_TOKEN invalid or Telegram unreachable')","typeGuard":"function isOkResponse(r: Response): r is Response & { ok: true } { return r.ok }","tryCatchPattern":"const r = await fetch(photoUrl)\nif (!r.ok) {\n  if (r.status === 404) { /* re-getFile, retry once */ }\n  throw new Error(`photo upstream failed: ${r.status}`)\n}","preventionTips":["Always re-resolve file_path via getFile immediately before download — Telegram file links expire (~1 hour).","Validate BOT_TOKEN at bot startup with getMe and fail fast.","Add monitoring for egress connectivity to api.telegram.org from the pod.","Distinguish upstream status codes in the error message instead of a generic 500."],"tags":["http","telegram","network","proxy-error"],"backgroundTag":"upstream-fetch-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}