hcengineering/platform · error

Failed to fetch photo

Error message

Failed to fetch photo

What it means

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.

Source

Thrown at services/telegram-bot/pod-telegram-bot/src/server.ts:172

    '/photo/:fileId',
    wrapRequest(async (req, res) => {
      const { fileId } = req.params
      const fileLink = await bot.telegram.getFileLink(fileId)

      const response = await fetch(fileLink.toString())
      if (!response.ok) {
        res.status(response.status).send(response.statusText)
        return
      }

      if (response.body != null) {
        res.setHeader('Content-Type', response.headers.get('Content-Type') ?? 'application/octet-stream')
        res.setHeader('Content-Length', response.headers.get('Content-Length') ?? '0')

        const stream = Readable.fromWeb(response.body as ReadableStream<any>)
        stream.pipe(res)
      } else {
        res.status(500).send('Failed to fetch photo')
      }
    })
  )

  app.use((err: any, _req: any, res: any, _next: any) => {
    if (err instanceof ApiError) {
      res.status(err.code).send({ code: err.code, message: err.message })
      return
    }

    res.status(500).send(err.message?.length > 0 ? { message: err.message } : err)
  })

  return app
}

export function listen (e: Express, ctx: MeasureContext, port: number, host?: string): Server {
  const cb = (): void => {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Verify the BOT_TOKEN environment variable is valid and the bot can call getMe.
  2. Check that the file_id/path parameter passed to the Telegram getFile call is current — re-resolve it via getFile before downloading.
  3. Test outbound connectivity from the pod to api.telegram.org (DNS/proxy/firewall).
  4. 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.

Example fix

// before
} else {
  res.status(500).send('Failed to fetch photo')
}
// after
} else {
  console.error('photo fetch failed', response.status)
  if (response.status === 404) { /* re-getFile and retry once */ }
  res.status(502).send(`Failed to fetch photo: upstream ${response.status}`)
}
Defensive patterns

Strategy: retry

Validate before calling

async function canReachTelegram(): Promise<boolean> {
  const r = await fetch(`https://api.telegram.org/bot${process.env.BOT_TOKEN}/getMe`)
  return r.ok
}
if (!(await canReachTelegram())) throw new Error('BOT_TOKEN invalid or Telegram unreachable')

Type guard

function isOkResponse(r: Response): r is Response & { ok: true } { return r.ok }

Try / catch

const r = await fetch(photoUrl)
if (!r.ok) {
  if (r.status === 404) { /* re-getFile, retry once */ }
  throw new Error(`photo upstream failed: ${r.status}`)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/1b6929f43ec39aae. Report an issue: GitHub.