Дискорду не нравится отсутствие заголовков при запросе (а именно юзер агент), передайте их.
url = str(ctx.author.avatar_url)
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, "avatar.jpg") # Сохраняю картинку.
Но лучше сделать как дает дискорд.ру:
author.avatar_url возвращает
Asset (у вас не так, судя по коду? Почему-то нет обертки avatar_url в str(), предположим просто опустили этот момент). Можно использовать метод save() встроенный. Уже интереснее:
with open('avatar.jpg', 'wb') as f:
await ctx.author.avatar_url.save(f)
Ну и третий вариант, вместо urllib использовать requests:
import requests
url = str(ctx.author.avatar_url)
r = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
with open('avatar.jpg', 'wb') as f:
f.write(r.content)