Lỗi này là gì
./main.go:12:15: multiple-value in single-value context
Build của bạn vừa thất bại với lỗi này. Trình biên dịch Go đang báo rằng một hàm trả về nhiều giá trị đang được dùng ở chỗ chỉ cho phép một giá trị. Go rất nghiêm ngặt về điều này — bạn không thể bỏ qua giá trị trả về một cách im lặng như trong Python hay JavaScript.
Nguyên nhân
Hầu hết các hàm Go có thể thất bại đều trả về (value, error). Trình biên dịch Go không cho phép bỏ qua các giá trị trả về. Khi bạn truyền thẳng kết quả của hàm đó vào một biểu thức, dùng làm đối số cho hàm khác, hoặc gán vào chỗ chỉ nhận một giá trị — lỗi này xuất hiện.
Tình huống phổ biến 1: Gán không khớp số giá trị
// SAI — os.Open trả về (*File, error)
f := os.Open("config.json")
// ĐÚNG
f, err := os.Open("config.json")
if err != nil {
log.Fatal(err)
}
Tình huống phổ biến 2: Truyền hàm nhiều giá trị trực tiếp vào hàm khác
// SAI — strconv.Atoi trả về (int, error)
fmt.Println(strconv.Atoi("42"))
// ĐÚNG
n, err := strconv.Atoi("42")
if err != nil {
log.Fatal(err)
}
fmt.Println(n)
Tình huống phổ biến 3: Dùng hàm nhiều giá trị trong biểu thức
// SAI
total := getValue() + 10
// Nếu getValue() được định nghĩa như sau:
func getValue() (int, error) {
return 42, nil
}
// ĐÚNG
val, err := getValue()
if err != nil {
log.Fatal(err)
}
total := val + 10
Tình huống phổ biến 4: Lồng các lời gọi hàm
// SAI — json.Marshal trả về ([]byte, error)
os.Stdout.Write(json.Marshal(data))
// ĐÚNG
b, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(b)
Cách sửa từng bước
Bước 1: Tìm dòng gây ra lỗi
Trình biên dịch chỉ ra chính xác file và dòng:
./main.go:12:15: multiple-value in single-value context
Đi đến dòng 12, cột 15. Tìm lời gọi hàm ở phía bên phải.
Bước 2: Kiểm tra chữ ký hàm
Hover chuột lên hàm trong editor hoặc xem tài liệu:
// Kiểm tra hàm trả về những gì
go doc os.Open
// Output: func Open(name string) (*File, error)
Bước 3: Nhận đủ tất cả các giá trị trả về
Mỗi giá trị trả về cần một biến — kể cả những giá trị bạn không định dùng:
// Dùng blank identifier _ để bỏ qua giá trị không cần
f, _ := os.Open("config.json")
Tuy nhiên, bỏ qua lỗi trong code production thường là ý tưởng tồi. Blank identifier phù hợp cho script nhanh hoặc khi bạn chắc chắn tuyệt đối rằng lỗi sẽ không xảy ra.
Bước 4: Tái cấu trúc code
Khi lời gọi hàm nhiều giá trị nằm sâu trong một biểu thức, hãy tách nó ra trước:
// Trước (bị lỗi)
result := process(strconv.Atoi(input))
// Sau (đã sửa)
n, err := strconv.Atoi(input)
if err != nil {
return fmt.Errorf("invalid input: %w", err)
}
result := process(n)
Trường hợp đặc biệt: Hàm trả về nhiều giá trị không phải lỗi
Không phải hàm nhiều giá trị nào cũng liên quan đến lỗi. Đôi khi bạn có dạng như:
func minMax(nums []int) (int, int) {
// ...
return min, max
}
// SAI
fmt.Println("Min:", minMax(nums))
// ĐÚNG
min, max := minMax(nums)
fmt.Println("Min:", min, "Max:", max)
Go cho phép truyền hàm nhiều giá trị trực tiếp vào hàm khác chỉ khi danh sách tham số của hàm nhận khớp hoàn toàn. Trường hợp này hiếm gặp và thường được tránh vì lý do dễ đọc — dùng tên biến tường minh giúp code dễ theo dõi hơn nhiều.
Kiểm tra sau khi sửa
go build ./...
Build thành công sẽ không in ra gì. Bạn cũng có thể chạy:
go vet ./...
để phát hiện các vấn đề liên quan đến kiểu dữ liệu khác trước khi đưa lên production.
Mẹo tránh lỗi này trong tương lai
- Dùng IDE hỗ trợ Go — GoLand hoặc VS Code với extension Go sẽ đánh dấu lỗi này trước cả khi bạn lưu file.
- Bật
golangci-linttrong CI pipeline. Công cụ này tự động phát hiện lỗi này và hàng chục vấn đề tương tự. - Không bao giờ nối chuỗi các hàm nhiều giá trị — luôn gán vào biến có tên trước, dù phải thêm một dòng. Bản thân bạn lúc debug lúc 2 giờ sáng sẽ cảm ơn điều này.
- Blank identifier là phương án cuối cùng — nếu bạn bỏ qua lỗi bằng
_, hãy thêm comment giải thích tại sao việc bỏ qua đó là an toàn.

