Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Разбор ошибок Go: что означает сообщение и как его исправить

Сообщение компилятора Go на английском выглядит сурово, но почти всегда объясняет проблему точно — просто словами, к которым привык не каждый. «declared and not used» — это не придирка, а предупреждение о потерянном значении. «cannot use x as string value» — не «неправильный тип», а требование явного преобразования. Ниже — словарь типичных ошибок и разбор каждой из них.

29 типовых ошибоксо ссылкой на урок

Ошибки, которые встречаются на второй неделе обучения

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

Вторая группа ошибок — из рантайма: выход за границы среза, разыменование nil-указателя, отправка в закрытый канал, гонка данных. Их общий признак в том, что код компилируется, а ломается позже — под нагрузкой или на edge case. Именно их чаще всего находят на собеседовании и в код-ревью.

Интерактивный разбор ниже берёт текст ошибки, который вы скопировали из терминала, и находит совпадение в словаре: объяснение, способ исправления, пример кода и ссылку на урок, где тема разобрана. Полный словарь продублирован ниже в виде таблицы — он остаётся полезным, даже если вы не пользуетесь JavaScript.

Небольшая практическая деталь: вставлять нужно ключевую часть сообщения, а не весь лог. Компилятор печатает несколько строк — путь к файлу, номер, суть и список переменных; разбору достаточно строки про суть, например declared and not used или index out of range. Паника из лога сервиса годится целиком: в ней уже есть и суть, и стек.

Вставьте текст ошибки

Подойдёт сообщение компилятора, go vet, паника или ошибка из логов — даже вместе с номерами строк.

Частые ошибки — в один клик

Как читать сообщение компилятора

  1. Сначала имя файла и строка. Go показывает путь файла, номер строки и колонки: ./main.go:18:13. Номер колонки указывает на конкретное выражение.
  2. Затем суть в квадратных скобках. Сообщение устроено как «что случилось»: declared and not used: total,cannot use x as string value, missing return.
  3. Потом контекст. В конце сообщения Go перечисляет переменные с их типами — это подсказка, почему преобразование не проходит.
  4. Ошибок может быть несколько. Компилятор показывает их списком: исправление первой часто исправляет и следующую.
Словарь собран по урокам базового и продвинутого курсов Go. Он покрывает ошибки компиляции, паники рантайма, проверки go vet и типовые проблемы данных.

Словарь типичных ошибок Go

Полный список разборов: сообщение, причина, исправление и урок.

cannot use ... as ... value

Ошибка компиляции
cannot use x (variable of type int) as string value in argument to fmt.Println

Компилятор не может преобразовать значение автоматически: в Go нет неявных приведений типов. int и string — разные типы, и даже если «на глаз» они выглядят одинаково, преобразовать их нужно явно.

Исправление: Используйте явное преобразование strconv.Itoa(x) для числа в строку или fmt.Sprint(x), а для сравнения приведите типы к общему заранее. Следите за типами элементов срезов и карт: срез []int нельзя сравнивать со срезом []string.

Типы данных: числа

undefined: имя

Ошибка компиляции
undefined: fmt.Println

Имя не найдено в текущей области видимости. Чаще всего это забытый импорт пакета (fmt, strings, errors) или опечатка в имени переменной. Если имя живёт в другом пакете — нужен импорт и префикс пакета.

Исправление: Добавьте импорт: import "fmt". Если компилятор подсказывает «declared and not used» рядом — проверьте, не перепутали ли вы имя переменной и имя пакета.

Пакеты и модули

declared and not used

Ошибка компиляции
declared and not used: total

В Go переменная, объявленная внутри функции, обязана быть использована. Это защита от «мусорных» переменных: если посчитали значение и забыли вывести или вернуть — почти наверняка это ошибка. На уровне пакета правило не действует.

Исправление: Либо используйте переменную, либо замените объявление на пустое присваивание _: _, err = doSomething(). Не обходите правило массово — оно ловит реальные опечатки.

Переменные и константы

missing return

Ошибка компиляции
missing return at end of function

Функция объявлена с возвращаемым значением, но не во всех ветвях есть return. Компилятор требует, чтобы каждая ветвь завершалась возвратом — даже если кажется, что ветвь недостижима.

Исправление: Добавьте return в конце функции или измените сигнатуру. Для функций с несколькими результатами проверьте все ветви: ранний return в середине часто «забывает» один из результатов.

Функции: параметры и возврат

non-boolean condition

Ошибка компиляции
non-boolean condition in if statement

В условии if ожидается выражение типа bool. В Go нет «истинности» для чисел и строк, как в некоторых других языках: нельзя написать if count или if name.

Исправление: Сравнивайте явно: if count > 0, if name != "". Для указателей проверяйте на nil: if p != nil.

Условия: if и else

assignment mismatch

Ошибка компиляции
assignment mismatch: 2 variables but fmt.Sprintf returns 1 value

Число переменных слева от = не совпадает с числом возвращаемых значений. Частая причина — предположение, что функция возвращает два значения, когда возвращает один, или наоборот.

Исправление: Сверьтесь с сигнатурой функции. Если функция возвращает (значение, ошибку), то присваивать нужно два переменные: v, err := f(). Если нужна только ошибка — используйте _, err := f().

Ошибки и несколько возвращаемых значений

cannot convert ... to type ...

Ошибка компиляции
cannot convert x (type int) to type string

Прямое преобразование int в string в Go недопустимо: это не перевод символа, а попытка переиначить целое число в текст. Нужно явно сказать, что имеется в виду.

Исправление: Для текста: strconv.Itoa(x) или string(rune(x)). Для парсинга: strconv.Atoi(s). Для байтов: string([]byte{...}).

Строки и руны

invalid operation: operator not defined

Ошибка компиляции
invalid operation: a + b (mismatched types int and string)

Оператор применён к типам, для которых он не определён: сложить число и строку, сравнить разные типы, применить арифметику к структуре.

Исправление: Приведите операнды к одному типу заранее. Для строк используйте конкатенацию через + только для строк, а числа переводите в текст через strconv.

Операторы и выражения

index out of range

Ошибка времени выполнения
panic: runtime error: index out of range [5] with length 3

Обращение к элементу за пределами среза или массива. В Go проверки границ нет: это решение ради скорости, и ошибка всплывает как паника в рантайме.

Исправление: Проверяйте границы перед обращением: if i < len(s). Для срезов не забывайте про slice bounds out of range при s[low:high] — high обязан быть не больше len, а low не больше high. В циклах используйте range, он безопаснее.

Срезы: создание и индексация

nil pointer dereference

Паника
panic: runtime error: invalid memory address or nil pointer dereference

Разыменование указателя, который равен nil. Обычно это структура с методом, вызванным на nil-указателе, или поле, которое не заполнили при создании объекта.

Исправление: Проверяйте err сразу после вызова, не забывайте инициализировать указатели и карты. Метод с приёмником-указателем умеет проверять nil сам: func (s *Service) Handle() { if s == nil { return } ... }.

Указатели

send on closed channel

Паника
panic: send on closed channel

В канал отправляют после close. Закрытый канал означает «новых значений не будет», поэтому отправка в него — нарушение контракта и паника.

Исправление: Закрывает канал всегда отправитель, и только когда он закончил отправлять. Если отправителей несколько, закрытие согласовывают через sync.Once или отдельный канал-сигнал. Получатель канал не закрывает.

Каналы: обмен данными

close of closed channel / close of nil channel

Паника
panic: close of closed channel

Канал закрывают повторно или закрывают nil-канал. Обе ситуации означают, что владение каналом нарушено: закрывать должен ровно один отправитель.

Исправление: Закрывайте канал в одном месте — обычно в функции-владельце после завершения всех отправок. Для повторного закрытия используйте sync.Once: once.Do(func() { close(ch) }).

Каналы: обмен данными

integer divide by zero

Паника
panic: runtime error: integer divide by zero

Целочисленное деление на ноль — паника рантайма. В отличие от вещественной арифметики, где результатом будет Inf или NaN, целые числа так не ведут себя.

Исправление: Проверяйте знаменатель до деления. Если ноль — это штатная ситуация, возвращайте ошибку: if div == 0 { return 0, errors.New("деление на ноль") }.

Типы данных: числа

sync: negative WaitGroup counter

Паника
panic: sync: negative WaitGroup counter

Счётчик WaitGroup ушёл в минус: Done() вызвали больше раз, чем Add(). Обычно это ошибка в учёте задач, когда часть воркеров завершается не по своему пути.

Исправление: Считайте задачи: wg.Add(n) до запуска, ровно один wg.Done() на задачу. Частая ошибка — defer wg.Done() внутри цикла без учёта, что горутина запускается на каждой итерации.

WaitGroup и синхронизация

all goroutines are asleep - deadlock

Ошибка времени выполнения
fatal error: all goroutines are asleep - deadlock!

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

Исправление: Добавьте таймаут: select с case <-time.After(d) или context.WithTimeout. Проверьте, что канал действительно кто-то читает и что все ветки select имеют путь выхода.

Select и таймауты

concurrent map writes

Ошибка данных
fatal error: concurrent map writes

Карту читали и меняли из разных горутин одновременно. Карта в Go не потокобезопасна: внутренняя структура может остаться в несогласованном состоянии, и рантайм аварийно останавливает программу.

Исправление: Закройте карту sync.RWMutex (или sync.Map, если паттерн доступа действительно такой). Не забывайте RLock на чтении. Копия карты (maps.Clone) тоже не решает проблему — нужна синхронизация.

Мьютексы и общее состояние

WARNING: DATA RACE

Ошибка данных
WARNING: DATA RACE
Write at 0x00c000104010 by goroutine 8:

Гонка данных: две горутины обращаются к одной переменной, хотя бы одна из них пишет, и нет синхронизации. Программа может работать «правильно» годами и сломаться в любой момент.

Исправление: Запустите тесты с детектором гонок: go test -race ./... Затем добавьте мьютекс, атомики или канал — в зависимости от того, что вы делаете с данными. Гонки не лечатся «случайным» порядком выполнения.

Мьютексы и общее состояние

assignment to entry in nil map

Ошибка времени выполнения
panic: assignment to entry in nil map

Запись в карту, которая не была создана через make. Чтение из nil-карты допустимо (вернёт нулевые значения), а запись — нет: рантайм паникует.

Исправление: Создайте карту: m := make(map[string]int). Для структуры с полем-картой инициализируйте его в конструкторе или через указатель на структуру (иначе все структуры поделят одну карту).

Карты map

context deadline exceeded

Ошибка времени выполнения
context deadline exceeded
goroutine 1 [running]: ...

Контекст отменили по таймауту (deadline) или явно (cancel). Внутри это обычная ошибка: запрос не успел завершиться в отведённое время либо вызывающая сторона прекратила работу.

Исправление: Проверяйте ctx.Err() и возвращайте ошибку наверх. Не забывайте cancel(): context.WithCancel требует вызвать cancel, иначе контекст и его ресурсы живут до конца родительского. Таймауты задавайте на уровне HTTP-клиента и базы данных.

Контекст: отмена и дедлайны

EOF / unexpected EOF

Ошибка времени выполнения
unexpected EOF

EOF — конец потока данных, это не ошибка, а нормальный сигнал «данных больше нет». unexpected EOF означает, что поток оборвался на полуслове: файл прочитан не до конца, соединение оборвалось, ответ сервера пришёл неполным.

Исправление: Различайте io.EOF и другие ошибки: if errors.Is(err, io.EOF) { return nil }. unexpected EOF обычно означает повреждённые данные или оборванное соединение — проверьте целостность файла и код ответа сервера.

Файлы и ввод-вывод

connection refused / no such host

Ошибка времени выполнения
dial tcp 127.0.0.1:5432: connect: connection refused

Соединение не установилось: сервис не слушает порт, DNS не резолвит имя или соединение упало по таймауту. В docker-compose это самая частая причина «сервис не видит базу».

Исправление: Проверьте, что процесс слушает порт, что адрес внутри контейнера — имя сервиса, а не localhost. Для Go добавьте таймаут на http.Client и context на запросы к базе.

HTTP-клиент

sql: no rows in result set

Ошибка данных
sql: no rows in result set

Запрос ожидал ровно одну строку (QueryRow + Scan), а строк не оказалось: записи нет или их несколько. Это не «сбой базы», а штатная ситуация, которую нужно обработать.

Исправление: Используйте err := db.QueryRow(...).Scan(&v) и проверяйте errors.Is(err, sql.ErrNoRows) — это не ошибка, а «не найдено». Если строк может быть много — используйте Query со строками.

Курс pgx: PostgreSQL на Go

interface conversion: interface is nil

Ошибка времени выполнения
panic: interface conversion: interface {} is nil, not *sql.DB

Из интерфейса попытались достать значение конкретного типа, а внутри интерфейса лежит nil. Значение с пустым интерфейсом (any) остаётся nil до присваивания непустого значения.

Исправление: Проверяйте интерфейс на nil перед приведением типа, используйте форму с двумя значениями (v, ok := x.(T)) и обрабатывайте ok == false. Не оборачивайте nil-указатель в интерфейс — проверяйте указатель.

Приведение типов и type assertion

cannot assign to s[0]

Ошибка компиляции
cannot assign to s[0]

Строка в Go неизменяема: это последовательность байтов, а не массив. Изменить символ в строке нельзя — компилятор запрещает это на этапе сборки.

Исправление: Соберите новую строку: []byte(s) для байтов, []rune(s) для символов. Для замены подстроки используйте strings.Replace, для склейки — strings.Builder.

Строки и руны

multiple-value ... in single-value context

Ошибка компиляции
multiple-value f() in single-value context

Функцию, возвращающую несколько значений, использовали там, где ожидается одно. Например, передали f() в другую функцию, которая принимает один аргумент.

Исправление: В Go передать несколько значений можно только в вызове с точно совпадающим числом параметров. В остальных случаях разнесите по переменным: a, b := f().

Функции: параметры и возврат

vet: printf format ... has arg of wrong type

go vet
fmt.Printf format %d has arg s of wrong type string

go vet нашёл несоответствие между строкой формата и типом аргумента. %d ждёт число, а вы передали строку. Код скомпилируется, но напечатает мусор в виде %!d(string=hello).

Исправление: Замените глагол на подходящий (%s для строк, %v для универсального вывода) или преобразуйте значение. Запускайте go vet ./... (или golangci-lint) в CI — такие ошибки ловятся до продакшена.

Тестирование в Go

requires go1.22 or later

Ошибка компиляции
predeclared any requires go1.18 or later (-lang was set to go1.17)

В коде используется возможность из более новой версии языка, а в go.mod указана более старая. Компилятор строго соблюдает версию языка из директивы go — это гарантирует, что проект соберётся старым инструментарием.

Исправление: Обновите директиву в go.mod: go mod edit -go=1.22 и обновите toolchain, если нужно. Новая версия языка включается только явным изменением go.mod — это осознанное решение команды разработчиков.

Пакеты и модули

constant ... truncated to integer

Ошибка компиляции
constant 1.5 truncated to integer

Дробное число присвоили целочисленной переменной или использовали в целочисленном контексте. Go не округляет молча — это было бы потерей данных.

Исправление: Явно выберите: округление math.Round(x), усечение int(x) или оставьте тип float64 и форматируйте при выводе.

Типы данных: числа

cannot use nil as int value

Ошибка компиляции
cannot use nil as int value in assignment

nil в Go — не универсальный «пустой указатель». Он допустим только для указателей, срезов, карт, каналов, функций и интерфейсов. Для int, string и struct это не имеет смысла.

Исправление: Используйте нулевое значение типа: 0, "", struct{}{}. Если поле структуры должно быть необязательным — сделайте его указателем или добавьте флаг «задано».

Переменные и константы

Частые вопросы об ошибках в Go

Почему в Go такая строгая проверка неиспользуемых переменных?

Потому что это ловит целый класс опечаток. Если вы посчитали total, но забыли его вывести или вернуть, почти наверняка где-то потерялось значение. Правило «объявленная переменная должна использоваться» работает только внутри функций: на уровне пакета переменные живут дольше и могут быть частью API.

Что делать с паникой index out of range?

Сначала найдите место: в Go паника сообщает номер строки и имя функции. Проверьте границы перед обращением и не забывайте, что у среза есть и длина, и ёмкость. Если индекс приходит извне — отсекайте его на входе, а не внутри обработчика. Циклы по срезу лучше писать через range: он не выйдет за границы.

Чем ошибка отличается от паники в Go?

Ошибка — это ожидаемый результат работы, который возвращается по соглашению (value, err) и обрабатывается вызывающим кодом. Паника — это аварийная остановка текущей горутины: она означает, что программа столкнулась с ситуацией, которую не предусмотрели. Панику можно перехватить через recover, но лечить ею ошибки не стоит: panic нужен для ситуаций уровня «программа не может продолжить работу», например, нарушен инвариант при инициализации.

Нужен ли go vet в повседневной работе?

Да, это самый дешёвый способ поймать ошибки форматирования и подозрительные конструкции. Запускайте go vet ./... локально и в CI, а перед релизом — golangci-lint с набором проверок. Большинство находок vet — это реальные баги: неверный глагол в Printf, потерянная копия мьютекса, неиспользуемый результат функции.

Где разобрать тему подробнее

Что внутри

Ошибки Go как словарь, а не как лог

Сообщение компилятора Go почти всегда указывает на конкретную причину: тип не реализует интерфейс, нельзя преобразовать, переменная не используется, вызов не совпадает с сигнатурой. Проблема в том, что новичок читает текст, а не понимает правило, которое за текстом стоит.

Словарь разбирает типовые ошибки на части: что означает сообщение, какое правило Go нарушено, минимальный исправленный пример и ссылка на урок, где это правило разобрано. Так сообщение перестаёт быть препятствием и становится точкой входа в тему.

Почему ошибок много в одном месте

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

Как пользоваться

  1. Вставьте текст ошибки целиком. Нужен весь блок сообщений, а не только последняя строка: по нему видно, от чего сообщения отталкиваются.
  2. Начните с первой ошибки в списке. Компилятор идёт сверху вниз, и каждая следующая ошибка часто является следствием предыдущей.
  3. Перейдите по ссылке на урок. Разбор ошибки объясняет правило, а не только этот случай: следующая похожая ошибка вы уже прочитаете сами.

Когда это пригождается

Сообщение на две страницы

Обычно это одна причина с большим последствием. Найдите первое сообщение — чаще всего оно и есть корень проблемы.

Ошибка появляется после добавления зависимости

Проверьте версию модуля и имя интерфейса: несовпадение версий даёт сообщения, которые выглядят как ошибки вашего кода.

Ошибка только в CI

Чаще всего расходятся версии Go или зависимостей между машиной и сборкой. Сравните go.mod и версию рантайма.

Словарь покрывает типовые ошибки компилятора и паники. Сообщения от инструментов статического анализа и от гонок данных имеют другой формат — для них нужен другой инструмент.