Skip to content
VLSI Mentor

Python Part I · Module 14 · Errors and Exception Handling

Syntax Errors vs Runtime Errors

Thirteen modules have quietly assumed the code works. This one is about what happens when it does not — and the first thing worth knowing is that Python's failures come in two kinds that are genuinely different, not two shades of the same thing. One is found by reading your file and is found every single time, on every machine, before a single instruction executes. The other is found only by running, only on the path the data actually takes, and can therefore sit in a script for six months and surface on the night a regression matters. Telling which one you are looking at takes about two seconds once you know what to look for, and it decides everything you do next.

Foundation14 min readPythonPart IFundamentalsErrorsDebugging

Part I · Module 14 · Chapter 14.1 · Syntax Errors vs Runtime Errors

1. Two Questions, Not One

When a script fails, there are two separate questions, and people often merge them:

  1. Could Python read the file at all?
  2. Did something go wrong while it ran?

A failure of the first kind is a syntax error. The file is not valid Python, so there is nothing to execute. A failure of the second kind is a runtime error: the file was perfectly readable, Python executed it, and an instruction could not be carried out.

The canonical pair is one character apart in difficulty and a world apart in behaviour:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
prnt("hi")     # NameError    - at run time
print("hi"     # SyntaxError  - before anything runs

The first is a real program. It runs, reaches that line, looks for a name called prnt, and fails. The second is not a program at all.

2. A Runtime Error Is a Normal Program That Fails

Here is the first one as a file:

Azvya Education Pvt. Ltd.VLSI Mentor
typo.py (BROKEN ON PURPOSE)
print("starting the run")
prnt("hi")
print("finished")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
starting the run
Traceback (most recent call last):
  File "typo.py", line 2, in <module>
    prnt("hi")
    ^^^^
NameError: name 'prnt' is not defined. Did you mean: 'print'?

Read what happened in order. Line 1 ran — starting the run is real output that reached the terminal. Line 2 failed. Line 3 never happened.

That is the signature of a runtime error: the program was alive. It did work, produced output, and then hit something it could not do. Python even guesses the fix here, because prnt is one deletion away from a name it knows.

3. A Syntax Error Means Nothing Ran At All

Now the other one. Note that the mistake is on line 4, and line 1 is an ordinary print that would obviously succeed:

Azvya Education Pvt. Ltd.VLSI Mentor
broken.py (BROKEN ON PURPOSE)
print("starting the run")
tests = ["smoke", "cdc"]
print(f"{len(tests)} tests queued")
print("about to finish"
print("done")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "broken.py", line 4
    print("about to finish"
         ^
SyntaxError: '(' was never closed

There is no starting the run in that output. Line 1 did not run. Neither did lines 2 or 3, all of which are flawless.

This is the fact to take from the chapter. Python reads and parses the entire file before executing any of it. If any part of the text is not valid Python, there is no program to run, so none of it runs — not the good lines, not the lines before the mistake, nothing.

4. Where Python Points, and Where the Mistake Is

Notice something in that output: the error is reported on line 4, but many people would describe the bug as "a missing bracket at the end of line 4" and expect line 5.

Python reports the line where the unclosed ( was opened, because that is the last place the file still made sense. The parser kept reading, found print("done") on line 5 where it expected a closing bracket, and reported back to the opening.

The practical rule: for a bracket or quote error, look at the reported line and the one after it. The mistake is almost always a boundary between the two.

Other syntax errors point exactly where you would expect:

Azvya Education Pvt. Ltd.VLSI Mentor
missing_colon.py (BROKEN ON PURPOSE)
runtime = 182.4
if runtime > 100
    print("slow")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "missing_colon.py", line 2
    if runtime > 100
                    ^
SyntaxError: expected ':'

That message names the fix. So does this one, which catches the single most common beginner slip in any C-like language:

Azvya Education Pvt. Ltd.VLSI Mentor
assign_in_if.py (BROKEN ON PURPOSE)
status = "PASS"
if status = "PASS":
    print("ok")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "assign_in_if.py", line 2
    if status = "PASS":
       ^^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

Chapter 9.3 made the = versus == distinction; this is Python refusing the confusion outright rather than silently assigning, which is exactly what a C compiler would have let you do.

5. Why Runtime Errors Are the Dangerous Kind

A syntax error is found every time, so it is found immediately, by you, on your machine. A runtime error is found only when execution reaches it with the data that triggers it — and that is a much weaker guarantee:

Azvya Education Pvt. Ltd.VLSI Mentor
first_three.py (BROKEN ON PURPOSE)
tests = ["smoke", "cdc"]

for i in range(3):
    print(tests[i])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
smoke
cdc
Traceback (most recent call last):
  File "first_three.py", line 4, in <module>
    print(tests[i])
          ~~~~~^^^
IndexError: list index out of range

Two iterations worked. The third did not. If tests had held three names this file would have run perfectly, and a reviewer reading it would most likely have approved it.

That is the whole risk profile of a runtime error:

  • it depends on the data, so a smoke test can miss it;
  • it depends on the path, so a branch nobody took in review can hide it;
  • it can therefore reach production and wait.

A script that parses a simulation log every night for six months and then meets its first truncated log has a runtime error that was always there.

6. IndentationError Is a Syntax Error

Python's whitespace rules are enforced by the parser, which means a bad indent behaves exactly like a bad bracket — nothing runs:

Azvya Education Pvt. Ltd.VLSI Mentor
bad_indent.py (BROKEN ON PURPOSE)
total = 0
for t in ["smoke", "cdc"]:
print(t)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "bad_indent.py", line 3
    print(t)
    ^^^^^
IndentationError: expected an indented block after 'for' statement on line 2

IndentationError is a subclass of SyntaxError — a more specific name for one family of parse failure. Everything in Section 3 applies to it unchanged. Chapter 9.2 covered what the indentation rules are; this is what happens when you break one.

7. Telling Them Apart in One Second

You do not have to reason about any of this when staring at a failure, because the two kinds of output look different in a way you can see instantly:

Syntax errorRuntime error
First line of output File "x.py", line NTraceback (most recent call last):
Any program output before it?neverusually
Depends on your data?noyes
Found on every run?yesonly on the path that triggers it
Fix lives inthe textthe logic or the data

The single fastest check is the first line. If the output opens with Traceback (most recent call last):, your program ran, and you are looking at a runtime error. If it opens with File "..." and a line number, nothing ran.

That header is the subject of the next chapter, and it is worth far more than it looks.

8. Predict, Run, Observe

What does this file print?

Azvya Education Pvt. Ltd.VLSI Mentor
predict.py (BROKEN ON PURPOSE)
print("A")
values = [1, 2, 3]
print("B", sum(values))
print("C", values[7])
print("D")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A
B 6
Traceback (most recent call last):
  File "predict.py", line 4, in <module>
    print("C", values[7])
               ~~~~~~^^^
IndexError: list index out of range

A and B printed. The program was alive and doing correct work, which is the runtime-error signature.

C printed nothing — not even the C label. The arguments to print are evaluated before print is called, so values[7] failed first and print never ran. That ordering trips people constantly.

D never happened. An uncaught runtime error ends the program.

Now change line 4 to print("C", values[7] — deleting one bracket — and the output becomes a SyntaxError with no A and no B. Same file, one character, completely different category.

9. Which One Do You Have?

A small decision procedure, which is really just Section 7 as steps:

  1. Did the script produce any of its own output? If yes, it is a runtime error — skip to the traceback.
  2. Does the first line say Traceback? If yes, runtime error.
  3. Otherwise it is a syntax error. Open the reported line and the line after it, and look for an unclosed bracket, a missing colon, a stray = or a bad indent.

The two halves of Module 14 follow this split. Chapters 14.2 and 14.3 are about reading runtime failures. Chapters 14.4 onward are about deciding which ones your script should handle.

10. Exercises

Exercise 1 — prove nothing runs. Write a four-line file whose first line prints something obvious and whose last line is missing a closing bracket. Run it. Record exactly how many characters of your own output appeared.

Exercise 2 — the opening bracket. Create an unclosed ( on line 2 of a six-line file. Note which line Python reports and explain in one sentence why it is not line 6.

Exercise 3 — four syntax errors. Produce, deliberately, a missing colon, a bad indent, an unclosed quote and = used in an if. Record each message. Which two name the fix outright?

Exercise 4 — data-dependent. Write a loop over a list that works for a three-item list and raises IndexError for a two-item one, without changing the loop. Explain which property of the run decided the outcome.

Exercise 5 — classify five. For each, say syntax or runtime before running it: (a) len("abc" (b) len(abc) (c) x = [1,2][5] (d) if x == 1 print(x) (e) int("12a").

Exercise 6 — argument evaluation. Explain, using Section 8, why print("C", values[7]) prints no C. Then write a two-line file that proves the same ordering with a different failing expression.

Exercise 7 — the subclass. Show that an IndentationError prevents an earlier print from running, exactly as a bracket error does. Say what that demonstrates about where indentation is enforced.

Exercise 8 — the six-month bug. Describe, in three sentences, a runtime error that could live in a nightly regression script for months. Say what input would finally trigger it and why no review would have caught it.

11. Summary

  • Python parses the whole file before running any of it, so a syntax error anywhere means nothing executes at all.
  • A syntax error is a property of the text. It does not depend on data, arguments or branch, and it is found on every run.
  • A runtime error is a property of the run. The file was valid, the program was alive, and an instruction could not be carried out.
  • Program output before the error means runtime error — the program was doing real work first.
  • The first output line is the fastest classifier: Traceback (most recent call last): means runtime; File "x.py", line N alone means syntax.
  • For bracket errors Python reports where the bracket opened, so read the reported line and the one after it.
  • IndentationError is a subclass of SyntaxError and behaves identically.
  • Runtime errors are the dangerous kind because they wait for the data and the path that trigger them.
  • Arguments are evaluated before the call, so a failing argument means the call never happens and its label never prints.

The next chapter takes that Traceback header seriously and turns the wall of text underneath it into three specific facts.

Where this fits

Part of the Python curriculum.