Types of error: syntax, logic and runtime

Nobody writes a whole program without making a mistake — not beginners, not the professionals who build the apps on your phone. What separates a good programmer from a frustrated one isn't avoiding errors; it's recognising them quickly. And to do that, it helps to know that not all errors are the same beast. In computing we sort them into three families, and each one announces itself — or hides — in a completely different way.

The order there is roughly "easiest to spot" → "hardest". A syntax error stops you before you even begin; a runtime error at least shouts when it happens; a logic error says nothing at all. Let's meet each one.

First, what "working" code looks like

Here is a tiny, correct program. It works out the average of three test marks. It obeys the grammar (so it runs), never asks for anything impossible (so it doesn't crash), and does the sum properly (so the answer is right). Press Run — the three families of error are all the ways this can go wrong.

function average(a: number, b: number, c: number): number { const total = a + b + c; return total / 3; // three marks, so divide by 3 } console.log("Average mark:", average(60, 72, 84));

Hold this picture in your head — runs, doesn't crash, right answer — because each kind of error takes one of those three things away.

1. Syntax errors — breaking the grammar

Every language has grammar. In English, "cat the sat mat on the" is made of real words but the arrangement is nonsense. Programming languages are far stricter: leave off a single bracket, quote mark or keyword and the computer simply cannot make sense of your instructions. That is a syntax error, and it stops your program from running at all — not one line executes.

Because broken code can't run, we can only look at it, not press Run. Spot the mistake below — the opening ( after if is never closed:

let mark = 72; if (mark >= 70 { // ← SYNTAX ERROR: the ( is never closed with ) console.log("Grade B"); }

The fix is to close the bracket: if (mark >= 70) {. Other classic syntax slips are a missing closing quote on a string ("hello), a missing curly brace }, or misspelling a keyword (fucntion instead of function).

The good news: syntax errors are the friendliest kind. Your editor usually underlines them in red before you even run the code, and the error message points almost exactly at the line. The computer refuses to guess what you meant — and that refusal is what keeps you safe.

2. Runtime errors — it runs, then crashes

A runtime error is sneakier. The grammar is perfect, so the program starts — but partway through it is asked to do something impossible, throws up its hands, and crashes. "Runtime" just means "while the program is running", as opposed to before it starts.

The best way to understand a runtime error is to see one. The code below tries to read the .length of a name that was never set — it is undefined, and undefined things have no length. Press Run and read the message that appears in the Output:

const names: string[] = ["Ada", "Alan", "Grace"]; // We meant names[2] — but there is no names[3], so this is undefined: const chosen = names[3]; console.log("The name has", chosen.length, "letters"); // 💥 crashes here: can't read ".length" of undefined

Notice what happened: the program printed nothing useful and stopped dead with an error message the moment it hit the impossible instruction. That message is a gift — it names the problem and the line, so you know exactly where to look. Other common runtime crashes are dividing by zero, reading an array index past the end, or calling a method that doesn't exist (myNumber.toUpperCae()).

The cure is usually a check before you leap — make sure the value exists first:

const names: string[] = ["Ada", "Alan", "Grace"]; const chosen = names[3]; if (chosen === undefined) { console.log("No name at that position — nothing to measure."); } else { console.log("The name has", chosen.length, "letters"); }

3. Logic errors — it runs, and lies to you

Here is the dangerous one. A logic error breaks no grammar and asks for nothing impossible, so the program runs perfectly from start to finish and hands you an answer. The trouble is the answer is wrong — and nothing tells you. No red underline, no crash, no error message. Just a quietly incorrect result.

Watch this. We want the average of three marks, but the programmer typed / 2 instead of / 3. It runs, it prints a number, and the number is nonsense (the "average" is bigger than every mark!). Press Run:

function average(a: number, b: number, c: number): number { const total = a + b + c; return total / 2; // BUG: three marks, but dividing by 2 } // The three marks are 60, 72 and 84 — the average must be between them (72). console.log("Average mark:", average(60, 72, 84)); // Prints 108 — larger than the biggest mark. Clearly wrong, but nothing complained.

The program was obedient — it did exactly what we wrote. It just wasn't what we meant. Here is the corrected version; the only change is the divisor:

function average(a: number, b: number, c: number): number { const total = a + b + c; return total / 3; // fixed: three marks → divide by 3 } console.log("Average mark:", average(60, 72, 84)); // now prints 72 — correct!

The lesson: a program that runs is not the same as a program that is right. Only by testing — checking the output against an answer you worked out yourself — do logic errors come to light.

Of the three families, the logic error is by far the most dangerous — precisely because the program still runs. A syntax error won't let you start; a runtime error crashes loudly and points at the line. But a logic error sails through silently and produces a confident, wrong answer. Nobody gets an error message. The bug in the spacecraft, the wrong bill, the exam mark that's off by one — these are almost always logic errors that ran perfectly.

That's why testing matters so much. The only way to catch a logic error is to run the program on inputs where you already know the right answer, and check the output matches. If average(60, 72, 84) gives 108 when you know it should be 72, you've found your bug — the computer never will.

Telling them apart at a glance

When something goes wrong, ask yourself two quick questions and you can name the family instantly:

Same three questions, every time. Learn them and debugging stops feeling like magic and starts feeling like detective work.