Skip to content

Unhandled fetch rejections crash gateway instead of graceful recovery #2487

@altryne

Description

@altryne

Problem

The gateway crashes repeatedly due to unhandled promise rejections from fetch/API calls. Network failures kill the entire process instead of gracefully handling errors.

Error Signature

[clawdbot] Unhandled promise rejection: TypeError: fetch failed
    at node:internal/deps/undici/undici:16416:13
    at processTicksAndRejections (node:internal/process/task_queues:104:5)

Observed Crash Context

  • After Telegram file upload (sending image)
  • After Telegram message send
  • After external API calls (Notion, etc.)

Root Cause

1. delivery.js - Unguarded Telegram API Calls (CRITICAL)

File: dist/telegram/bot/delivery.js:105-174

All media send operations have no error handling:

// Line 110-112 - NO try/catch!
await bot.api.sendPhoto(chatId, file, { ...mediaParams });

Compare to send.js which correctly wraps calls with retry logic:

// send.js:231 - properly wrapped
result = await requestWithDiag(() => api.sendPhoto(chatId, file, mediaParams), "photo")
  .catch((err) => { throw wrapChatNotFound(err); });

Affected operations in delivery.js:

  • bot.api.sendAnimation() (line 105)
  • bot.api.sendPhoto() (line 110)
  • bot.api.sendVideo() (line 115)
  • bot.api.sendVoice() (line 131)
  • bot.api.sendAudio() (line 165)
  • bot.api.sendDocument() (line 171)

2. telegram.js Onboarding - Unguarded Fetch

File: dist/channels/plugins/onboarding/telegram.js:60

const res = await fetch(url);  // ← NO try/catch!
const data = (await res.json().catch(() => null));  // Only JSON parsing caught

3. Global Handler Exits Process

File: dist/infra/unhandled-rejections.js:27

process.exit(1);  // Kills gateway on ANY unhandled rejection

Crash Flow

Network timeout during bot.api.sendPhoto()
         ↓
Promise rejection (no .catch() in delivery.js)
         ↓
Bubbles to global unhandledRejection handler
         ↓
process.exit(1)
         ↓
Gateway crashes

Expected Behavior

Network failures should be:

  1. Caught and logged with context (URL, method, error details)
  2. Retried if appropriate (using existing createTelegramRetryRunner)
  3. Returned as an error to the caller
  4. Never crash the gateway

Suggested Fix

Wrap delivery.js API calls like send.js does:

const request = createTelegramRetryRunner({ retry: opts.retry });

// Instead of:
await bot.api.sendPhoto(chatId, file, mediaParams);

// Use:
await request(() => bot.api.sendPhoto(chatId, file, mediaParams), "photo");

Files to Modify

File Priority Fix
src/telegram/bot/delivery.ts CRITICAL Wrap bot.api.* calls with retry runner
src/channels/plugins/onboarding/telegram.ts HIGH Wrap fetch in try/catch
src/infra/unhandled-rejections.ts MEDIUM Consider not exiting on transient network errors

Environment

  • Node.js v25.4.0
  • clawdbot installed globally via npm

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions