Troubleshooting
Diagnose and fix common issues with @igniter-js/bot. Error codes, debugging techniques, webhook problems, and platform-specific troubleshooting.
Troubleshooting
Common issues and solutions when building bots with @igniter-js/bot.
Build Errors
"At least one adapter is required"
Symptom: .build() throws: At least one adapter is required.
Cause: No adapters were added before calling .build().
Fix:
// Before
const bot = IgniterBot.create().build(); // ❌
// After
const bot = IgniterBot.create()
.addAdapter('telegram', telegram({ token: '...' }))
.build(); // ✅"Bot handle is required"
Symptom: .build() throws: Bot handle is required.
Cause: Neither a global handle (withHandle()) nor a per-adapter handle was configured.
Fix:
// Option 1: Global handle
IgniterBot.create().withHandle('@mybot')
// Option 2: Per-adapter handles
IgniterBot.create().addAdapter('telegram', telegram({
token: '...',
handle: '@mybot',
}))Runtime Errors
PROVIDER_NOT_FOUND
Symptom: BotError: Provider 'xxx' not found
Cause: An incoming request targets an adapter that wasn't registered, or the route's dynamic segment doesn't match.
Fix:
- Check that the adapter key in your route matches the key used in
addAdapter():// bot setup .addAdapter('telegram', tg) // route — must match app.all('/api/bot/telegram', ...) // 'telegram' must match - If using
nextRouteHandlerAdapter, ensure the[adapter]segment matches:/api/bot/telegram/assistant_bot → adapter = 'telegram'
CLIENT_NOT_PROVIDED
Symptom: BotError: Telegram/WhatsApp/Discord client not provided
Cause: The adapter's internal HTTP client initialization failed. This usually happens when configuration values are empty or invalid.
Fix:
- Check that environment variables are set and accessible at runtime
- Verify adapter configuration values are not empty strings:
// Debug: log config (never log tokens in production!) const tg = telegram({ token: process.env.TELEGRAM_TOKEN! }); console.log('Token exists:', !!process.env.TELEGRAM_TOKEN);
CONTENT_TYPE_NOT_SUPPORTED
Symptom: BotError: Poll content not supported by adapter 'whatsapp'
Cause: Attempting to send a content type that the adapter's capabilities don't include.
Fix:
- Check the adapter capability table
- Use conditional logic:
async handle(ctx) { if (ctx.provider === 'whatsapp') { await ctx.reply('Polls are not available on WhatsApp. Reply with your choice: A, B, or C.'); } else { await ctx.bot.send({ provider: ctx.provider, channel: ctx.channel.id, content: { type: 'poll', question: '...', options: ['A', 'B', 'C'] }, }); } }
INVALID_COMMAND_PARAMETERS
Symptom: Command handler receives unexpected arguments or resolves with an error.
Cause: The args Zod schema failed validation against user input.
Fix:
- Check the Zod schema is correct:
args: z.object({ query: z.string(), // Remember: strings without .optional() are required }) - Test your schema manually:
import { z } from 'zod'; const schema = z.object({ query: z.string().min(2) }); schema.parse({ query: 'hi' }); // ✅ passes schema.parse({}); // ❌ throws ZodError - Provide clear error messages in
help:help: 'Use /search <query> [category: books|movies|music]',
Webhook Issues
Telegram: Webhook Not Receiving Updates
Symptoms: Bot is running but doesn't respond to messages.
Checklist:
- Verify webhook URL is publicly accessible:
curl https://your-domain.com/api/bot/telegram/assistant_bot - Check webhook status via Telegram API:
Look forcurl "https://api.telegram.org/bot<TOKEN>/getWebhookInfo""ok": trueand"url": "https://..."and nolast_error_message. - Ensure HTTPS certificate is valid — Telegram requires HTTPS.
- Delete and re-set the webhook manually:
curl "https://api.telegram.org/bot<TOKEN>/deleteWebhook" curl "https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://..." - Check the
dropPendingUpdatessetting — iffalse, old updates may be processed first, making it seem like new messages don't work.
Discord: 401 Unauthorized on Interactions
Symptoms: Discord returns "401 Unauthorized" for bot interactions.
Checklist:
- Verify
publicKeyis correct — copy it from the Discord Developer Portal (General Information). - Enable
X-Signature-Ed25519header forwarding in your proxy/reverse proxy configuration. - Check that the raw request body is being forwarded — some middleware (body parsers) may modify the body before the adapter reads it.
WhatsApp: Messages Not Being Delivered
Symptoms: Bot receives webhooks but outbound messages don't arrive.
Checklist:
- Verify the phone number ID format:
whatsapp({ phone: '1234567890' }) // Just the number, no + or spaces - Check the message template status — some message types require pre-approved templates.
- Verify the recipient has opted in — WhatsApp requires users to message first.
- Check rate limits — WhatsApp Cloud API has strict rate limits (80 messages/second for business accounts).
Session Issues
Sessions Not Persisting
Symptoms: ctx.session.data is always empty or reset to defaults.
Checklist:
- Use
ctx.session.update()not direct mutation:// ✅ Works await ctx.session.update({ step: 'checkout' }); // ❌ May not persist ctx.session.data.step = 'checkout'; - Check that the session store is configured:
// Must be set before .build() builder.withSessionStore(memoryStore()) - If using memory store in production: It doesn't persist across restarts. Use Redis or Prisma.
Redis Sessions Timeout
Symptoms: Sessions expire too quickly in Redis.
Checklist:
- Check the TTL in your Redis store implementation — the default is 24 hours if
expiresAtis not set. - Verify Redis connection is stable — check for connection drops or timeouts.
- Check for Redis maxmemory eviction — if
maxmemory-policyis set toallkeys-lru, sessions may be evicted.
Rate Limiting Issues
Users Being Rate Limited Too Quickly
Symptoms: Legitimate users hit rate limits within seconds.
Checklist:
- Check your
windowMsandmaxRequests:rateLimitMiddleware({ maxRequests: 10, windowMs: 60_000, // 1 minute — not 1 second! }) - Use the correct key generator:
// Per-user (default) keyGenerator: (ctx) => `${ctx.provider}:${ctx.message.author.id}` // Per-user-per-command (more granular) keyGenerator: (ctx) => { const cmd = ctx.message.content?.type === 'command' ? ctx.message.content.command : 'message'; return `${ctx.provider}:${ctx.message.author.id}:${cmd}`; } - Memory store is per-process — if you have multiple instances, each has its own counter. Use Redis for shared state.
TypeScript Issues
"Property does not exist on type BotContext"
Symptom: TypeScript error when accessing enriched context properties.
Fix: Middleware enrichment flows through the builder's generic types:
// The builder tracks middleware return types
const bot = IgniterBot.create()
.addMiddleware(async (ctx, next) => {
await next();
return { user: { id: '123' } }; // returned
})
.addCommand('profile', {
async handle(ctx) {
ctx.user.id; // ✅ TypeScript knows about this
},
})
.build();If you're accessing enriched context in a separate file, cast as needed:
type EnrichedContext = BotContext & { user: { id: string } };
async handle(ctx) {
const enriched = ctx as EnrichedContext;
console.log(enriched.user.id);
}Debugging Tips
Enable Verbose Logging
import { loggingPresets } from '@igniter-js/bot';
builder.addMiddleware(loggingPresets.debug());This logs every message, command, error, and metric with full detail.
Check Adapter Status
console.log('Registered adapters:', Object.keys(bot.getAdapters()));
console.log('Telegram adapter:', bot.getAdapter('telegram'));Test Webhook Manually
# Simulate a Telegram message (use actual token and chat ID)
curl -X POST "https://your-domain.com/api/bot/telegram/assistant_bot" \
-H "Content-Type: application/json" \
-d '{"update_id": 1, "message": {"message_id": 1, "from": {"id": 123, "is_bot": false, "first_name": "Test"}, "chat": {"id": 123, "type": "private"}, "date": 1234567890, "text": "/start"}}'Inspect Session Data
builder.addMiddleware(async (ctx, next) => {
console.log('Session data:', ctx.session.data);
await next();
});Still Stuck?
- Check the API Reference for exact method signatures
- Review Best Practices for common patterns
- Read the adapter source code at
packages/bot/src/adapters/— it's well-documented - Enable debug logging with
loggingPresets.debug()for full request tracing