Ba loại lỗi — cú pháp, lúc chạy, và sai kết quả
Không chạy nổi dòng nào, chạy rồi dừng giữa chừng, chạy hết nhưng ra sai. Ba dấu hiệu quan sát được ngay, ứng với ba loại lỗi cần ba cách chẩn đoán khác hẳn nhau.
TL;DR: Một chương trình sai luôn để lộ dấu hiệu trước khi bạn kịp đọc tên lỗi. Không chạy nổi dòng nào, kể cả dòng in đầu tiên, là cú pháp: máy đọc hết file trước khi chạy dòng đầu. Chạy được một lúc rồi dừng giữa chừng bằng một khối chữ đỏ là runtime: câu lệnh hợp lệ nhưng gặp đúng dữ liệu nó không xử lý nổi. Chạy trọn vẹn, không báo gì, nhưng kết quả sai là logic — loại duy nhất không ai báo cho bạn, vì máy chỉ làm đúng thứ bạn viết, không phải thứ bạn muốn. Ba dấu hiệu này quyết định bạn soi ở đâu trước.
Bạn gõ xong ba mươi dòng code cho bài tính điểm trung bình lớp. Bấm chạy. Màn hình đứng im, không một chữ nào hiện lên — kể cả dòng print đầu tiên bạn viết ngay dòng thứ hai, thứ đáng lẽ phải chạy trước tất cả những dòng còn lại.
Bạn sửa một chỗ, chạy lại. Lần này màn hình động đậy, in ra vài dòng, rồi giữa chừng nhảy ra một khối chữ đỏ và dừng hẳn.
Bạn vá tiếp, chạy lại lần ba. Chương trình chạy trơn tru từ đầu tới cuối, không một dòng đỏ nào, in ra đúng một con số duy nhất — chỉ có điều con số đó sai.
Bài này lo đúng một việc: nhìn vào dấu hiệu bạn vừa thấy (không phải tên lỗi tiếng Anh bạn tra được) để xếp nó vào một trong ba loại, và biết ngay nên soi ở đâu trước.
1. Analogy — ba kiểu hỏng của một chuyến xe
Bạn hẹn đi chơi xa bằng xe máy. Ba chuyến đi, ba kiểu trục trặc khác hẳn nhau.
Chuyến thứ nhất: bạn đề mãi mà xe không nổ máy. Không nhích được một mét nào — kể cả đoạn đường bằng phẳng nhất ngay trước nhà.
Chuyến thứ hai: xe nổ máy, bạn chạy được một đoạn, rồi giữa đường xe khựng lại và tắt máy hẳn. Bạn đã đi được một phần đường, nhưng chưa tới nơi.
Chuyến thứ ba: xe chạy êm ru từ đầu tới cuối, không khục khặc lần nào, bạn tới nơi đúng giờ — chỉ có điều đó là nhà một người khác. Địa chỉ nhập sai ngay từ lúc xuất phát, và suốt chuyến đi không có tín hiệu nào báo cho bạn biết.
| Chuyến xe | Chương trình |
|---|---|
| Xe không đề nổ máy được | Cú pháp: không dòng nào chạy |
| Xe chạy được một đoạn rồi tắt máy giữa đường | Runtime: dừng giữa chừng |
| Xe tới đích êm ru nhưng tới nhầm địa chỉ | Logic: chạy hết, không báo gì, nhưng sai |
Muốn biết chương trình đang hỏng kiểu nào, hỏi đúng ba câu theo thứ tự: nó có chạy được dòng nào không? Nếu có, nó có chạy hết không? Nếu chạy hết, kết quả có đúng không?
2. Ba dấu hiệu, chưa cần đọc tên lỗi
Trước khi tra bất kỳ tên lỗi tiếng Anh nào, màn hình đã cho bạn đủ thông tin để xếp loại — chỉ cần nhìn chương trình chạy tới đâu.
| Loại lỗi | Dấu hiệu bạn thấy | Vì sao |
|---|---|---|
| Cú pháp | Không chạy nổi dòng nào — kể cả dòng in đầu tiên | Máy phải đọc hiểu hết file trước khi chạy dòng đầu |
| Runtime | Chạy được một lúc rồi dừng giữa chừng, có output rồi mới nổ | Câu lệnh hợp lệ nhưng gặp dữ liệu nó không xử lý nổi |
| Logic | Chạy hết, không báo gì, nhưng kết quả sai | Máy làm đúng thứ bạn viết, chỉ là bạn viết nhầm thứ mình muốn |

Cú pháp là tập quy tắc viết đúng chính tả của một ngôn ngữ lập trình — dấu hai chấm sau if, thụt lề đúng chỗ, ngoặc đóng đủ cặp. Nhìn đoạn code sau: dòng thứ hai là một print hoàn toàn bình thường, dòng thứ ba là một if thiếu mất dấu hai chấm ở cuối.
diem = 6
print("Bat dau kiem tra diem")
if diem >= 5
print("Dat")
Dòng if diem >= 5 thiếu mất dấu hai chấm ở cuối. Khi chạy đoạn code này, dòng in đầu tiên — Bat dau kiem tra diem — có xuất hiện trên màn hình không? Viết dự đoán của bạn ra giấy trước khi đọc tiếp.
Chạy thử, output thật:
File "bai.py", line 3
if diem >= 5
^
SyntaxError: expected ':'
Dòng Bat dau kiem tra diem không hề xuất hiện — mặc dù dòng print đó đứng ngay trước dòng lỗi và bản thân nó không có gì sai. Đây là bằng chứng rõ nhất của cú pháp: máy đọc toàn bộ file để kiểm tra cấu trúc có hợp lệ trước, rồi mới bắt đầu chạy dòng đầu tiên. Một chỗ sai chính tả ở dòng 3 chặn đứng cả dòng 2, dù dòng 2 chẳng dính líu gì tới lỗi đó.
Cách chẩn đoán tương ứng: soi đúng dòng máy chỉ ra và dòng ngay phía trên nó. Với cú pháp, chỗ sai gần như luôn nằm ở một trong hai dòng đó, vì đó là nơi máy dừng đọc.
3. Loại đắt nhất — vì sao không ai báo cho bạn?
Cú pháp và runtime có một điểm chung: máy tự dừng lại và báo cho bạn biết chỗ nó vấp. Logic thì không.
Máy không suy diễn ý định của người viết — nó chỉ làm đúng, đủ, những gì được viết ra. Lỗi logic sống đúng ở khoảng trống đó: bạn viết đúng cú pháp, máy chạy đúng như bạn viết, chỉ có điều thứ bạn viết không phải thứ bạn muốn, và máy không có cách nào biết được sự khác biệt đó để báo cho bạn.
Ví dụ cụ thể, cùng họ với lỗi bạn đã gặp ở module trước:
Đổi < thành <= ở điều kiện dừng làm vòng lặp chạy thừa đúng một vòng, và cả hai cách viết đều hợp lệ, đều chạy êm — máy không có cách nào biết bạn muốn dừng ở đâu.
tong = 0
buoi = 1
while buoi < 5:
tong = tong + buoi
buoi = buoi + 1
print(tong)
Chương trình này định tính tổng số buổi học từ buổi 1 tới buổi 5.
Vòng lặp cộng dồn buoi vào tong khi buoi < 5, bắt đầu từ buoi = 1. Đoán trước khi xem: dòng cuối chương trình in ra số nào?
Chạy thử, output thật:
10
Không một dòng đỏ nào. Không cảnh báo nào. Chương trình chạy trọn vẹn từ dòng đầu tới dòng cuối, đúng như nó được viết ra. Nhưng 10 chỉ là tổng của 1 cộng 2 cộng 3 cộng 4 — buổi thứ 5 chưa bao giờ được cộng vào, vì điều kiện buoi < 5 dừng vòng lặp ngay khi buoi chạm 5, trước khi thân vòng lặp kịp chạy thêm một lần nữa. Tổng đúng phải là 15.
Cách chẩn đoán tương ứng: vì không có dòng đỏ nào để soi, công cụ đúng là kéo bảng trace ra và chạy tay từng vòng lặp — chỉ bảng trace mới lộ ra đúng chỗ giá trị lệch khỏi con số bạn kỳ vọng.
4. Thử đoán — dữ liệu người dùng gõ vào
Chương trình dưới đây hỏi tuổi rồi in lại. Nếu người dùng gõ "hai muoi" thay vì "20", chuyện gì xảy ra? Chương trình có in được dòng nhắc "Nhap tuoi: " không? Có in được dòng "Tuoi cua ban la ..." không? Viết dự đoán của bạn ra giấy trước khi đọc tiếp.
tuoi = int(input("Nhap tuoi: "))
print("Tuoi cua ban la", tuoi)
Output thật khi gõ "hai muoi":
Nhap tuoi: Traceback (most recent call last):
File "bai.py", line 1, in <module>
tuoi = int(input("Nhap tuoi: "))
ValueError: invalid literal for int() with base 10: 'hai muoi'
Chương trình đã chạy — nó in ra được dòng nhắc "Nhap tuoi: " và chờ người dùng gõ. Nhưng dòng "Tuoi cua ban la ..." không bao giờ xuất hiện. input() trả về đúng chuỗi 'hai muoi' như người dùng gõ; int(...) nhận chuỗi đó và không có cách nào chuyển nó thành số, nên dừng chương trình giữa chừng bằng tên lỗi ValueError ở dòng cuối. Câu lệnh int(...) hoàn toàn hợp lệ về cú pháp — nó chỉ gặp đúng loại dữ liệu nó không xử lý nổi.
Cách chẩn đoán tương ứng: với runtime, việc đầu tiên là xem đúng dữ liệu nào vừa đi vào ngay trước dòng bị báo — ở đây là chuỗi 'hai muoi' từ input() — và tên lỗi ở dòng cuối cùng đã gọi thẳng tên vấn đề.
5. Tới lượt bạn
Đoạn code dưới đây chấm điểm cho một lớp có 3 học sinh. Đừng chạy thử trong đầu bằng cách nhớ lại hai ví dụ trên — tự đếm số dòng output, rồi tự trả lời hai câu: đoạn này thuộc loại nào trong ba loại vừa học, và bạn sẽ soi ở đâu trước để chẩn đoán?
diem = [8, 6, 9]
print("Cham diem xong roi")
print(diem[0])
print(diem[1])
print(diem[2])
print(diem[3])6. Bẫy thường gặp
❌ Nhầm 1 — thấy chữ đỏ liền kết luận "chắc là cú pháp". Runtime cũng in ra một khối chữ đỏ y hệt cú pháp về hình thức.
✅ Đếm xem có dòng output nào xuất hiện trước khối chữ đỏ. Có, dù chỉ một dòng, là runtime, không phải cú pháp.
❌ Nhầm 2 — chạy xong không thấy chữ đỏ nào, kết luận ngay "chương trình đúng rồi".
✅ Không có chữ đỏ chỉ loại trừ cú pháp và runtime. Logic vẫn có thể sai hoàn toàn — cách duy nhất biết được là đối chiếu kết quả với con số bạn biết trước là đúng.
7. 📚 Đào sâu — ba dấu hiệu này không phải mẹo riêng của Python
Ba loại lỗi này không phải quy ước riêng của Python. Mọi ngôn ngữ có bước đọc chương trình trước khi chạy — Java, C, JavaScript — đều tách lỗi theo đúng ba trục này, chỉ khác tên gọi. Cú pháp bị trình biên dịch bắt trước khi chạy nổi một dòng nào; runtime ném ra một exception, cùng ý tưởng dữ liệu không xử lý nổi; logic thì không ngôn ngữ nào chặn được, vì máy không đọc được ý định người viết.
Vì vậy, bài này dạy bạn nhìn dấu hiệu quan sát được thay vì học thuộc tên lỗi Python — thứ cần nhớ khi chuyển ngôn ngữ là ba dấu hiệu: không chạy nổi dòng nào, dừng giữa chừng, hay chạy hết mà sai.
8. Liên hệ các bài khác
- Bài 2 — Đọc thông báo lỗi — mổ xẻ cách đọc traceback đầy đủ, thứ bài này mới chỉ chỉ vào dòng cuối cùng.
- Bài 3 — Sửa bằng giả thuyết — vòng đoán rồi kiểm chứng, dùng đúng ba cách chẩn đoán vừa học ở đây làm điểm xuất phát.
- Recall liên quan: Bài 1 module 1 (máy không suy diễn ý định) và Bài 5 module 2 (lặp lồng nhau) — cả hai vừa giải thích vì sao logic là loại đắt nhất trong ba loại.
9. Tóm tắt
- Chữ đỏ không tự động là cú pháp — runtime cũng đỏ y hệt, phân biệt bằng việc trước đó có in được gì hay chưa.
- Không có chữ đỏ không chứng minh chương trình đúng. Logic là loại duy nhất im lặng hoàn toàn, chỉ lộ ra khi bạn tự đối chiếu kết quả với con số bạn biết trước là đúng.
- Ba dấu hiệu này không phải mẹo riêng của Python — chúng áp dụng y hệt cho Java và mọi ngôn ngữ khác, đó là thứ đáng nhớ hơn cả cú pháp báo lỗi cụ thể.
10. Tự kiểm tra
- Q1Bạn chạy một chương trình, màn hình không hiện một chữ nào — kể cả dòng print đầu tiên. Loại lỗi nào đang xảy ra, và vì sao chính dòng print đầu tiên cũng bị chặn dù bản thân nó không sai?
- Q2Hai chương trình đều in ra một khối chữ đỏ khi chạy. Chương trình A không in được gì trước đó; chương trình B đã in ra ba dòng bình thường rồi mới đỏ. Loại lỗi của A và B khác nhau thế nào, và dấu hiệu nào cho bạn biết?
- Q3Đoạn code ở mục 3 định tính tổng 5 buổi nhưng chạy xong in ra
10, không phải15, và không báo lỗi gì. Vì sao đây là ví dụ chuẩn của lỗi logic, không phải runtime? - Q4Vì sao lỗi logic được gọi là loại đắt nhất trong ba loại, nếu nhìn từ cách máy thực thi lệnh mà bạn đã học ở module 1?
- Q5Chương trình hỏi tuổi bằng int(input(...)); người dùng gõ chữ thay vì số. Output thật cho thấy dòng nhắc "Nhap tuoi: " vẫn xuất hiện trước khi chương trình dừng. Điều đó chứng tỏ gì về loại lỗi này?
- Q6Bạn chạy chương trình, không có dòng chữ đỏ nào xuất hiện. Điều đó có đủ để kết luận chương trình đúng không? Vì sao?
- Q7Bạn vừa xếp một lỗi vào loại runtime — in được hai dòng rồi dừng giữa chừng bằng ValueError. Bước chẩn đoán tiếp theo là gì, và vì sao đó là cách đúng cho runtime, không phải cho cú pháp hay logic?
Bài tiếp theo: Đọc thông báo lỗi
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên