Программирование с ИИ
ENRU
2.12

Чтение трейсбека

находим виноватую строку за тридцать секунд

Что это

Трейсбек (traceback) — это то, что Python печатает, когда программа упала. Выглядит пугающе, читается за полминуты и почти всегда прямо называет виноватую строку.

Навык полезен вдвойне: ты сразу понимаешь, что случилось, и можешь передать ИИ не «не работает», а точную причину. Второе важнее — от этого зависит, починит он с первого раза или с пятого.

Как выглядит в коде

Вот программа, которая запрашивает погоду и показывает температуру:

import json


def fetch_weather(city: str) -> dict:
    # Ответ сервера, когда город не найден
    raw = '{"cod": "404", "message": "city not found"}'
    return json.loads(raw)


def format_report(data: dict) -> str:
    temp = data["main"]["temp"]
    return f"Сейчас {temp} градусов"


def handle_command(city: str) -> str:
    data = fetch_weather(city)
    return format_report(data)


if __name__ == "__main__":
    print(handle_command("Tonsberg"))

Запускаем — и получаем:

измерено
Traceback (most recent call last):
  File "/tmp/tbdemo/weather2.py", line 21, in <module>
    print(handle_command("Tonsberg"))
          ~~~~~~~~~~~~~~^^^^^^^^^^^^
  File "/tmp/tbdemo/weather2.py", line 17, in handle_command
    return format_report(data)
  File "/tmp/tbdemo/weather2.py", line 11, in format_report
    temp = data["main"]["temp"]
           ~~~~^^^^^^^^
KeyError: 'main'

Как это читать

Правило первое: читай снизу вверх. Последняя строка — суть, всё остальное — как мы сюда попали.

измерено
KeyError: 'main'

KeyError значит: обратились к ключу словаря, которого там нет. Ключ назван прямо — 'main'.

Правило второе: предпоследний блок — место аварии.

измерено
  File "/tmp/tbdemo/weather2.py", line 11, in format_report
    temp = data["main"]["temp"]
           ~~~~^^^^^^^^

Файл, строка 11, функция format_report. Значки под строкой — подсказка Python (с версии 3.11): ^^^^ указывает ровно на ту часть выражения, которая упала. Здесь видно, что проблема в ["main"], а не в ["temp"]. Это важно: без значков пришлось бы гадать, какое из двух обращений виновато.

Правило третье: блоки сверху вниз — это путь вызовов. Программа начала со строки 21, оттуда зашла в handle_command (строка 17), оттуда в format_report (строка 11), и там упала. Этот путь отвечает на вопрос «откуда взялись данные», а он почти всегда и есть настоящая причина.

Правило четвёртое: ищи свои файлы. В реальном трейсбеке половина строк — из библиотек: site-packages, aiogram, asyncio. Их можно пролистать. Смотри на последнюю строку, где упомянут твой файл: ошибка почти всегда там, даже если упало внутри библиотеки.

Что мы поняли

Строка 11 не виновата — она честно делает то, что просили. Виновата строка 6: сервер вернул ошибку «город не найден», а код решил, что получил погоду. Место падения и причина ошибки — разные места. Трейсбек показывает первое, а путь вызовов помогает найти второе.

Как сказать ИИ

Самая частая ошибка — пересказывать ошибку своими словами.

Не так
не работает, вылетает ошибка с ключом

ИИ придётся гадать, какой ключ, в каком файле и на какой строке. Он начнёт переписывать всё подряд.

Правильно — отдать трейсбек целиком, добавив то, чего в нём нет: что ты делал и чего ожидал.

Лучше
Падает при запросе погоды для несуществующего города.
Ожидал ответ «Город не найден», получил исключение.

Traceback (most recent call last):
  File "weather.py", line 21, in <module>
    print(handle_command("Tonsberg"))
  File "weather.py", line 17, in handle_command
    return format_report(data)
  File "weather.py", line 11, in format_report
    temp = data["main"]["temp"]
           ~~~~^^^^^^^^
KeyError: 'main'

Почини так, чтобы ошибка сервера обрабатывалась в fetch_weather,
а не всплывала в format_report.

Последняя фраза — важная. Без неё ИИ, скорее всего, обернёт строку 11 в try/except или подставит data.get("main", {}). Падать перестанет, пользователь получит «Сейчас None градусов», и настоящая ошибка спрячется.

На что смотреть

ИИ глушит ошибку вместо того, чтобы её обработать. Признаки в сгенерированном коде: except Exception: pass, except: return None, .get(key, {}) вместо проверки. Код перестаёт падать, но начинает врать.

ИИ правит место падения, а не причину. Если он изменил только ту строку, на которую указывал трейсбек, спроси: «откуда в эту функцию пришли неверные данные?»

Обрезанный трейсбек. Если скопировать только последнюю строку, ИИ не увидит пути вызовов и будет чинить вслепую. Копируй целиком, от слова Traceback.

Трейсбек из асинхронного кода длиннее и путанее. В нём будут кадры asyncio и внутренности библиотеки. Правило то же: ищи последнюю строку со своим файлом.

Шпаргалка

  • Читай снизу вверх: последняя строка — что случилось.

  • Предпоследний блок — файл и строка падения.

  • ^^^^ под строкой указывает на виноватую часть выражения.

  • Пролистывай чужие файлы, ищи последний свой.

  • Место падения ≠ причина. Причину ищи выше по пути вызовов.

  • ИИ отдавай трейсбек целиком плюс что делал и чего ожидал.