Skip to main content
Common issues and solutions for Towns bots.

Bot Doesn’t Respond

Check:
  1. APP_PRIVATE_DATA and JWT_SECRET are correct
  2. ✅ Webhook URL is accessible: https://your-bot.com/webhook
  3. ✅ Bot is installed in the space (Space Settings → Bots)
  4. ✅ Forwarding setting is correct:
    • “All Messages” - Receives everything
    • “Mentions, Commands, Replies & Reactions” (Default) - Only @mentions, commands, replies
    • “No Messages” - Receives nothing
Test webhook:
curl https://your-bot.com/health  # Should respond

Lost Context Between Events

The bot framework is stateless. Each event is isolated - no access to previous messages or conversation history.
Solution: Store context externally:
const messageCache = new Map()

bot.onMessage(async (handler, event) => {
  messageCache.set(event.eventId, event.message)
  
  if (event.replyId) {
    const original = messageCache.get(event.replyId)
    // Now you have the context
  }
})
For production, use a database. See Storing State.

Transaction Errors

Error: insufficient funds for gas
Problem: Your bot treasury wallet (bot.appAddress) needs ETH for gas. Solution:
import { formatEther } from 'viem'

// 1. Check balance
const balance = await bot.viem.getBalance({ address: bot.appAddress })
console.log(`Balance: ${formatEther(balance)} ETH`)
console.log(`Fund this address: ${bot.appAddress}`)

// 2. Send ETH to bot.appAddress from any wallet

// 3. Verify
const newBalance = await bot.viem.getBalance({ address: bot.appAddress })
Fund bot.appAddress (the app contract), not bot.botId (the signer). See Wallet Architecture.

Can’t Mention Users

// ❌ Wrong
await handler.sendMessage(channelId, "@username hello")

// ✅ Correct
await handler.sendMessage(channelId, "Hello <@0x1234...>", {
  mentions: [{
    userId: "0x1234...",
    displayName: "username"
  }]
})

Slash Commands Not Showing

Fix:
// Pass commands to makeTownsBot
import commands from './commands'
const bot = await makeTownsBot(privateData, jwtSecret, { commands })

// Restart bot after changing commands

Rate Limiting

Use a better RPC:
const bot = await makeTownsBot(privateData, jwtSecret, {
  baseRpcUrl: 'https://base-mainnet.g.alchemy.com/v2/YOUR_KEY'
})
Get free RPC from Alchemy or Infura.

Bot Crashes

Add error handling:
bot.onMessage(async (handler, event) => {
  try {
    await someAsyncOperation()
  } catch (error) {
    console.error('Error:', error)
    await handler.sendMessage(event.channelId, 'Something went wrong')
  }
})

Next Steps