Sửa lỗi 'undefined' và 'undeclared name' khi biên dịch Go

beginner🔷 Go2026-03-18| Go 1.18+, mọi hệ điều hành (Linux, macOS, Windows), go build / go run

Error Message

undefined: SomeVariable hoặc undeclared name: SomeFunction (khi biên dịch)
#go#biên-dịch#undefined#undeclared

Lỗi là gì

Bạn chạy go build hoặc go run và trình biên dịch dừng lại đột ngột:

./main.go:12:5: undefined: SomeVariable
./main.go:20:10: undefined: SomeFunction
./utils.go:8:3: undeclared name: helperFunc

Không có cảnh báo. Không biên dịch một phần. Chỉ là dừng hoàn toàn. Go từ chối tạo binary khi bất kỳ thứ gì được tham chiếu không tìm thấy — và đây thực ra là tính năng, không phải lỗi.

Tại sao điều này xảy ra

Không giống Python hay JavaScript, Go resolve mọi tên tại thời điểm biên dịch. Một khai báo bị thiếu không phải là bất ngờ lúc runtime — trình biên dịch bắt ngay lập tức. Điều đó có nghĩa là lỗi đánh máy, package sai, sai phạm vi, và file thiếu đều hiện ra dưới dạng lỗi undefined trước khi code của bạn chạy.

Các nguyên nhân thường gặp:

  • Lỗi đánh máy trong tên (chữ hoa/thường quan trọng — myFuncMyFunc là khác nhau)
  • Thiếu import cho package bạn đang gọi
  • Biến được khai báo trong một block, dùng ngoài block đó
  • Tên viết thường (unexported) được truy cập từ package khác
  • File bị loại trừ âm thầm bởi build tag
  • Khai báo package sai trong file

Giải pháp từng bước

1. Kiểm tra lỗi đánh máy trước

Thành thật mà nói: đây là nguyên nhân khoảng 70% trường hợp. Go phân biệt chữ hoa/thường, vì vậy myFunc, MyFunc, và myfunc là ba tên hoàn toàn khác nhau.

// SAI
result := myFuncion()  // lỗi đánh máy: thiếu 't' trong 'Function'

// ĐÚNG
result := myFunction()

So sánh cách viết tại cả nơi khai báo và nơi gọi. Trong VS Code hoặc GoLand, di chuột lên gạch chân đỏ — tooltip thường hiển thị tên gần nhất khớp, giúp lỗi đánh máy trở nên rõ ràng ngay lập tức.

2. Kiểm tra các import

Gọi hàm từ package khác mà không import, Go báo lỗi undefined — ngay cả khi package đó là một phần của thư viện chuẩn.

// SAI — thiếu import
func main() {
    fmt.Println("hello")  // undefined: fmt
}

// ĐÚNG
import "fmt"

func main() {
    fmt.Println("hello")
}

Cách nhanh nhất là dùng goimports:

goimports -w .

VS Code với extension Go và GoLand đều tự động thêm import còn thiếu khi lưu, vì vậy vấn đề này chủ yếu chỉ xảy ra với người dùng text editor thông thường.

3. Kiểm tra tên package, không chỉ import path

Đây là bẫy thường gặp: bạn import đúng path, nhưng gọi với tên sai.

import "math/rand"

// SAI — tên package là 'rand', không phải 'math'
result := math.Intn(100)  // undefined: math

// ĐÚNG
result := rand.Intn(100)

Định danh bạn dùng là tên của package, không phải import path. Khi chúng khác nhau (như math/randrand), kiểm tra khai báo package ở đầu file nguồn để xem tên thực tế.

4. Kiểm tra phạm vi — Biến có ở nơi bạn nghĩ không?

Phạm vi block trong Go rất nghiêm ngặt. Khai báo biến bên trong block if, for, hoặc bất kỳ block {} nào, và nó đơn giản là không tồn tại bên ngoài block đó.

// SAI
if condition {
    result := compute()
}
fmt.Println(result)  // undefined: result

// ĐÚNG — khai báo trước block
var result int
if condition {
    result = compute()
}
fmt.Println(result)

Toán tử khai báo ngắn := chính là phần gây nhầm lẫn. Bên trong block, result := compute() tạo ra một biến hoàn toàn mới có phạm vi trong block đó. Trông giống như gán cho biến bên ngoài, nhưng thực ra không phải.

5. Tên được export và không được export giữa các package

Quy tắc visibility của Go rất đơn giản: chữ cái đầu viết hoa = exported (public), viết thường = unexported (package-private). Cố gắng gọi tên unexported từ bên ngoài package sẽ nhận được lỗi undefined.

// Trong package 'utils':
func helperFunc() string { ... }  // unexported — chỉ thấy trong 'utils'
func HelperFunc() string { ... }  // exported — thấy ở khắp nơi

// Trong main.go:
utils.helperFunc()  // undefined: utils.helperFunc
utils.HelperFunc()  // OK

Nếu bạn sở hữu package, viết hoa tên để export nó. Nếu là package bên thứ ba, tìm phiên bản exported tương đương hoặc API surface khác.

6. File bị loại trừ bởi build tag

Build tag có thể âm thầm loại trừ file khỏi quá trình biên dịch. Khi file đó chứa các khai báo mà file khác phụ thuộc vào, bạn nhận được lỗi undefined mà không có lý do rõ ràng.

//go:build linux

package mypackage

func PlatformSpecificFunc() { ... }

Build trên macOS hoặc Windows và PlatformSpecificFunc không tồn tại theo góc nhìn của trình biên dịch. Kiểm tra các tag //go:build (hoặc cú pháp cũ hơn // +build) ở đầu file. Để xem chính xác file nào được đưa vào:

go list -f '{{.GoFiles}}' .
go list -f '{{.IgnoredGoFiles}}' .  # các file bị loại trừ bởi build tag

7. Khai báo package sai trong file

Mỗi file .go trong một thư mục phải khai báo cùng tên package (ngoại trừ file test). Một dòng package không khớp khiến trình biên dịch coi file đó thuộc package khác — vì vậy các khai báo của nó trở nên vô hình với các file xung quanh.

// file: utils.go
package util   // SAI nếu package của bạn được đặt tên là 'utils'

// ĐÚNG
package utils

Chạy go vet ./... — lệnh này tự động bắt lỗi này và một số lỗi không khớp tương tự.

8. Circular import ẩn giấu các khai báo

Go cấm hoàn toàn circular import. Nếu package A import B và B import A, cả hai đều không biên dịch được. Đôi khi lỗi hiện ra dưới dạng undefined cho các symbol trong package được import thay vì thông báo circular import rõ ràng. Gỡ rối vòng lặp này bằng cách chuyển các type dùng chung vào package thứ ba mà cả A và B đều không import lẫn nhau.

Xác nhận sau khi sửa

Sau khi thực hiện thay đổi, hãy build lại sạch để chắc chắn:

# Xóa cache build và build lại mọi thứ
go clean -cache
go build ./...

# Hoặc chạy trực tiếp
go run main.go

# Kiểm tra tất cả package trong module
go vet ./...

Output sạch (không có thông báo gì) nghĩa là thành công. Vẫn thấy lỗi? Đọc kỹ output của trình biên dịch — nó bao gồm đường dẫn file chính xác và số dòng. Không bao giờ mơ hồ, và luôn chỉ đúng chỗ.

Danh sách kiểm tra nhanh

  • Viết đúng tên, kể cả chữ hoa/thường?
  • Đã import package định nghĩa nó?
  • Dùng đúng alias package (ví dụ: rand, không phải math)?
  • Khai báo biến ở đúng phạm vi?
  • Tên được export (viết hoa) nếu dùng từ package khác?
  • Không có build tag nào âm thầm loại trừ file?
  • Tất cả file trong thư mục có cùng tên package?

Công cụ hữu ích

# Tự động sửa import
goimports -w .

# Phân tích tĩnh (bắt nhiều lỗi liên quan đến undefined)
go vet ./...
staticcheck ./...

# Xem chính xác file nào được biên dịch
go list -f '{{.GoFiles}}' .
go list -f '{{.IgnoredGoFiles}}' .  # các file bị loại trừ bởi build tag

Language server gopls — được dùng bởi VS Code, Vim/Neovim, và GoLand — đánh dấu tên undefined theo thời gian thực, trước khi bạn chạy trình biên dịch. Cài đặt một lần và hầu hết các lỗi này sẽ được bắt ngay lúc bạn gõ, không phải vài phút sau khi build.

Related Error Notes