if user_balances.get(author.id, initial_balance) < amount:
await ctx.send('Insufficient funds.')
else:
user_balances[author.id] -= amount
Ты не рассматриваешь ситуацию, когда пользователь не имеет сохранённого баланса (для него нет записи в user_balances), и но при этом amount <= initial_balance.
В этом случае :
1. get() вернёт initial_balance, так как записи о пользователе нет
2. условие в if не выполнится, так как initial_balance >= amount
3. управление перейдёт в else
4. обращение user_balances[author.id] провалится, так как записи о пользователе нет.
Рекомендую ознакомиться с методом словаря
setdefault(), который добавляет ключ в словарь, если его там не было.
Но я бы не заморачивался, а использовать вместо простого словаря класс
collections.defaultdict. Он при попытке прочитать несуществующий ключ добавляет его, вызывая указанную функцию для получения значения.
Например:
from collections import defaultdict
balance = defaultdict(lambda: 100) # по умолчанию значение 100
print(balance) # выведет defaultdict(..., {}) - в словаре пока ничего нет
print(balance["Вася"]) # выведет 100 - ключ "Вася" добавится автоматически
print(balance) # выведет defaultdict(..., {'Вася': 100}) - ключ сохранился в словаре
balance['Петя'] += 50 # создаст ключ "Петя" со значением 100, потом добавит к нему 50
print(balance) # выведет defaultdict(..., {'Вася': 100, 'Петя': 150})
Для своего бота код приспособь сам. Я не знаю, как ты сохраняешь/загружаешь баналс пользователей между запусками бота, но тебе может потребоваться сделать из defaultdict обычный словарь при сохранении, и наоборот - при загрузке.
Вообще я бы не советовал начинать изучения Питона с ботов - это НЕ простая тема.