> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/grammyjs/grammY/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Error types and error handling in grammY

grammY provides three main error types to help you handle different failure scenarios in your bot.

## BotError\<C>

Thrown when middleware throws an error. Wraps the original error and provides access to the context.

```typescript theme={null}
class BotError<C extends Context = Context> extends Error {
  constructor(
    public readonly error: unknown,
    public readonly ctx: C
  )
}
```

### Properties

* **`error`**: The original error that was thrown by your middleware
* **`ctx`**: The context object being processed when the error occurred
* **`message`**: A descriptive error message
* **`name`**: Always `"BotError"`
* **`stack`**: Stack trace from the original error (if available)

### When it occurs

`BotError` is thrown whenever an error occurs during middleware execution:

```typescript theme={null}
bot.on('message', (ctx) => {
  throw new Error('Something went wrong!')  // Will be wrapped in BotError
})

bot.on('message', async (ctx) => {
  await riskyOperation()  // If this throws, it becomes BotError
})
```

### Handling BotError

Use `bot.catch()` to install an error handler:

```typescript theme={null}
import { BotError } from 'grammy'

bot.catch((err: BotError) => {
  const ctx = err.ctx
  console.error(`Error while handling update ${ctx.update.update_id}:`)
  
  const e = err.error
  
  if (e instanceof GrammyError) {
    console.error('Error in request:', e.description)
  } else if (e instanceof HttpError) {
    console.error('Could not contact Telegram:', e)
  } else {
    console.error('Unknown error:', e)
  }
})
```

### Accessing context from errors

The `ctx` property lets you access update information when an error occurs:

```typescript theme={null}
bot.catch(async (err: BotError) => {
  const ctx = err.ctx
  
  // Notify user about the error
  if (ctx.chat) {
    await ctx.reply('Sorry, an error occurred while processing your request.')
  }
  
  // Log detailed information
  console.error('Error details:', {
    updateId: ctx.update.update_id,
    userId: ctx.from?.id,
    chatId: ctx.chat?.id,
    error: err.error
  })
})
```

### Example: Retry on error

```typescript theme={null}
bot.catch(async (err: BotError) => {
  if (err.error instanceof GrammyError) {
    if (err.error.error_code === 429) {
      // Rate limited - retry after delay
      const retryAfter = err.error.parameters.retry_after ?? 30
      console.log(`Rate limited. Retrying after ${retryAfter}s`)
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
      // Re-handle the update
      await bot.handleUpdate(err.ctx.update)
    }
  }
})
```

## GrammyError

Thrown when the Telegram Bot API returns an error response.

```typescript theme={null}
class GrammyError extends Error implements ApiError {
  public readonly ok: false = false
  public readonly error_code: number
  public readonly description: string
  public readonly parameters: ResponseParameters
  
  constructor(
    message: string,
    err: ApiError,
    public readonly method: string,
    public readonly payload: Record<string, unknown>
  )
}
```

### Properties

* **`error_code`**: Telegram's error code (e.g., 400, 404, 429)
* **`description`**: Human-readable error description from Telegram
* **`parameters`**: Additional parameters (e.g., `retry_after` for rate limits)
* **`method`**: The API method that was called (e.g., `"sendMessage"`)
* **`payload`**: The parameters passed to the API method
* **`ok`**: Always `false`

### Common Error Codes

| Code | Description                                   |
| ---- | --------------------------------------------- |
| 400  | Bad Request - invalid parameters              |
| 401  | Unauthorized - invalid bot token              |
| 403  | Forbidden - no permission to perform action   |
| 404  | Not Found - resource doesn't exist            |
| 409  | Conflict - bot is already running elsewhere   |
| 429  | Too Many Requests - rate limited              |
| 500  | Internal Server Error - Telegram server issue |

### When it occurs

`GrammyError` is thrown when Telegram's API returns an error:

```typescript theme={null}
try {
  await ctx.reply('Hello!')
} catch (err) {
  if (err instanceof GrammyError) {
    console.error('Telegram API error:', err.description)
  }
}
```

### Example: Handling specific errors

```typescript theme={null}
import { GrammyError } from 'grammy'

try {
  await bot.api.sendMessage(chatId, text)
} catch (err) {
  if (err instanceof GrammyError) {
    switch (err.error_code) {
      case 400:
        console.error('Bad request:', err.description)
        break
      
      case 403:
        console.error('Bot was blocked by user')
        // Remove user from database
        break
      
      case 429:
        const retryAfter = err.parameters.retry_after
        console.error(`Rate limited. Retry after ${retryAfter}s`)
        break
      
      default:
        console.error('Telegram error:', err)
    }
  }
}
```

### Example: Handling message deletion

```typescript theme={null}
try {
  await ctx.deleteMessage()
} catch (err) {
  if (err instanceof GrammyError) {
    if (err.description.includes('message to delete not found')) {
      console.log('Message already deleted')
    } else if (err.description.includes("message can't be deleted")) {
      console.log('Message is too old to delete')
    } else {
      throw err  // Re-throw unknown errors
    }
  }
}
```

### Example: Handling bot blocking

```typescript theme={null}
bot.on('message', async (ctx) => {
  try {
    await ctx.reply('Hello!')
  } catch (err) {
    if (err instanceof GrammyError) {
      if (err.error_code === 403) {
        console.log(`User ${ctx.from.id} blocked the bot`)
        // Update user status in database
        await db.users.update(ctx.from.id, { blocked: true })
      }
    }
  }
})
```

### ResponseParameters

Some errors include additional parameters:

```typescript theme={null}
interface ResponseParameters {
  migrate_to_chat_id?: number      // Group migrated to supergroup
  retry_after?: number             // Seconds to wait before retry
}
```

**Example:**

```typescript theme={null}
try {
  await ctx.reply('Message')
} catch (err) {
  if (err instanceof GrammyError) {
    if (err.parameters.migrate_to_chat_id) {
      const newChatId = err.parameters.migrate_to_chat_id
      console.log(`Chat migrated to ${newChatId}`)
      // Update chat ID in database
    }
  }
}
```

## HttpError

Thrown when the HTTP request to Telegram's servers fails.

```typescript theme={null}
class HttpError extends Error {
  constructor(
    message: string,
    public readonly error: unknown
  )
}
```

### Properties

* **`error`**: The underlying network error
* **`message`**: Descriptive error message
* **`name`**: Always `"HttpError"`

### When it occurs

`HttpError` is thrown when:

* Network connection fails
* DNS resolution fails
* Request times out
* Connection is aborted
* API transformer function throws

```typescript theme={null}
import { HttpError } from 'grammy'

try {
  await bot.api.sendMessage(chatId, 'Hello')
} catch (err) {
  if (err instanceof HttpError) {
    console.error('Network error:', err.error)
    // Retry logic here
  }
}
```

### Example: Retry on network failure

```typescript theme={null}
import { HttpError } from 'grammy'

async function sendWithRetry(chatId: number, text: string, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await bot.api.sendMessage(chatId, text)
    } catch (err) {
      if (err instanceof HttpError) {
        console.log(`Network error. Retry ${i + 1}/${maxRetries}`)
        if (i === maxRetries - 1) throw err  // Last retry failed
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
      } else {
        throw err  // Not a network error
      }
    }
  }
}
```

## Error Handling Patterns

### Global Error Handler

Handle all errors in one place:

```typescript theme={null}
import { BotError, GrammyError, HttpError } from 'grammy'

bot.catch((err: BotError) => {
  const e = err.error
  
  if (e instanceof GrammyError) {
    console.error('API Error:', e.description)
    
    if (e.error_code === 401) {
      console.error('Invalid bot token!')
      process.exit(1)
    }
  } else if (e instanceof HttpError) {
    console.error('Network error:', e)
  } else {
    console.error('Unknown error:', e)
  }
})
```

### Local Error Handling

Handle errors in specific middleware:

```typescript theme={null}
bot.command('start', async (ctx) => {
  try {
    await ctx.reply('Welcome!')
  } catch (err) {
    console.error('Failed to send welcome message:', err)
    // Don't crash the bot, just log it
  }
})
```

### Error Boundaries

Create protected middleware sections:

```typescript theme={null}
bot.errorBoundary(
  (err) => {
    console.error('Error in admin commands:', err)
  },
  adminCommands.middleware()
)
```

### Ignore Specific Errors

Silently handle expected errors:

```typescript theme={null}
bot.on('message', async (ctx) => {
  try {
    await ctx.deleteMessage()
  } catch (err) {
    if (err instanceof GrammyError) {
      // Ignore "message not found" errors
      if (!err.description.includes('message to delete not found')) {
        throw err
      }
    } else {
      throw err
    }
  }
})
```

### Custom Error Classes

Create application-specific errors:

```typescript theme={null}
class UserNotFoundError extends Error {
  constructor(public userId: number) {
    super(`User ${userId} not found`)
    this.name = 'UserNotFoundError'
  }
}

bot.on('message', async (ctx) => {
  const user = await db.findUser(ctx.from.id)
  if (!user) {
    throw new UserNotFoundError(ctx.from.id)
  }
  // ...
})

bot.catch((err: BotError) => {
  if (err.error instanceof UserNotFoundError) {
    console.log('New user:', err.error.userId)
    // Create user in database
  }
})
```

## Error Logging

Log errors with context:

```typescript theme={null}
bot.catch(async (err: BotError) => {
  const errorLog = {
    timestamp: new Date().toISOString(),
    updateId: err.ctx.update.update_id,
    userId: err.ctx.from?.id,
    chatId: err.ctx.chat?.id,
    errorType: err.error?.constructor.name,
    errorMessage: err.error instanceof Error ? err.error.message : String(err.error),
    stack: err.stack
  }
  
  console.error(JSON.stringify(errorLog, null, 2))
  
  // Send to external logging service
  await logger.error(errorLog)
})
```

## Best Practices

1. **Always set an error handler**
   ```typescript theme={null}
   bot.catch((err) => {
     console.error('Bot error:', err)
   })
   ```

2. **Handle specific errors explicitly**
   ```typescript theme={null}
   if (err instanceof GrammyError && err.error_code === 403) {
     // User blocked the bot
   }
   ```

3. **Don't leak sensitive information**
   ```typescript theme={null}
   // ❌ Bad - might expose token in logs
   console.error(err)

   // ✅ Good - sanitize error
   console.error(err.description)
   ```

4. **Implement retry logic for transient errors**
   ```typescript theme={null}
   if (err instanceof HttpError || err.error_code >= 500) {
     // Retry
   }
   ```

5. **Fail gracefully**
   ```typescript theme={null}
   try {
     await ctx.reply('Result')
   } catch {
     await ctx.reply('Sorry, something went wrong')
   }
   ```

Proper error handling makes your bot robust and reliable!
