Error Types & Exception Names

20 common programming errors explained in plain English — what they mean, when you see them, what causes them, and how to think about fixing them.

Last reviewed:

SyntaxError

Compile-time

The code is not written according to the language rules — the parser cannot understand it at all.

Detected by the interpreter/compiler before the code runs. The program never starts.

// Python
print("Hello"
       ↑ SyntaxError: unexpected EOF — missing closing parenthesis

// JavaScript
const x = {name: "Alice" age: 30}
                       ↑ SyntaxError: missing comma
  • Missing parenthesis, bracket, or semicolon
  • Misspelled keyword (`funtion` instead of `function`)
  • Mismatched quotes or brackets
  • Invalid indentation in Python
SyntaxError is the easiest category to fix — your IDE highlights it immediately. It is also the one most beginners encounter first.

TypeError

Runtime

An operation is performed on a value of the wrong type — like calling a number as if it were a function.

At runtime, when the code tries to do something a value does not support.

// JavaScript
const num = 42;
num();  // TypeError: num is not a function

const user = null;
console.log(user.name);
  // TypeError: Cannot read properties of null
  • Calling a non-function as a function
  • Accessing properties on `null` or `undefined`
  • Expecting a string but receiving an array
  • Wrong argument type passed to a function
Extremely common in JavaScript due to dynamic typing. TypeScript was created largely to catch TypeErrors before runtime.

ReferenceError

Runtime

The code refers to a variable or function that does not exist (has not been declared).

At runtime, when the JavaScript engine looks up a name and cannot find it in scope.

// JavaScript
console.log(userName);
  // ReferenceError: userName is not defined

function greet() {
  const msg = "Hello";
}
console.log(msg);
  // ReferenceError: msg is not defined (out of scope)
  • Using a variable before declaring it (`let`/`const` in the temporal dead zone)
  • Typo in a variable name
  • Accessing a variable outside its scope
  • Forgetting to import a module
Very common cause: writing `funciton` or `calback` (typo) — the correct name is not found.

NullPointerException

Runtime

The code tries to use an object reference that points to nothing (null). Mostly a Java / Kotlin / C# concept.

At runtime, when a method is called or a field is accessed on a null reference.

// Java
String name = null;
System.out.println(name.length());
  // NullPointerException: Cannot invoke "String.length()" because "name" is null

// The equivalent in JavaScript is a TypeError:
const user = null;
user.getName();  // TypeError: Cannot read properties of null
  • A method returns null in an edge case you didn't handle
  • Optional value not checked before use
  • Object not initialised before use
  • Database query returned no result and the null was not handled
Tony Hoare, who invented null in 1965, called it his "billion-dollar mistake". Modern languages (Kotlin, Swift, Rust) have null-safety built in to prevent this class of error.

IndexOutOfBoundsException

Runtime

The code tries to access an element in an array (or list) at an index that does not exist.

At runtime, when accessing `array[i]` where `i` is negative or ≥ the array length.

// Java
List<String> items = List.of("a", "b", "c");
items.get(5);
  // IndexOutOfBoundsException: Index 5 out of bounds for length 3

// Python equivalent
items = ["a", "b", "c"]
items[5]
  // IndexError: list index out of range
  • Off-by-one error (`i <= length` instead of `i < length`)
  • Accessing `items[items.size()]` instead of `items[items.size() - 1]`
  • Loop boundary not matching the collection size
  • Empty array not handled before access
The "off-by-one" bug is so universal that programmers joke: "There are two hard things in CS: naming things, cache invalidation, and off-by-one errors."

StackOverflowError

Runtime

The call stack ran out of space — usually caused by infinite recursion (a function that keeps calling itself forever).

At runtime, when the maximum call stack depth is exceeded.

// JavaScript
function countDown(n) {
  return countDown(n - 1);  // no base case!
}
countDown(100000);
  // RangeError: Maximum call stack size exceeded

// Java equivalent in recursive code
// java.lang.StackOverflowError
  • Recursive function with no base case (or an incorrect one)
  • Mutual recursion: A calls B, B calls A, infinitely
  • Extremely deep (but valid) recursion exceeding language limits
StackOverflow.com took its name from this error — it's one of the most universally recognised bugs. The fix is almost always: add a base case to your recursion, or refactor to iteration.

OutOfMemoryError

Runtime

The application tried to allocate more memory (heap space) than the system has available.

At runtime, when allocation fails — typically during intensive data processing or due to a memory leak.

// Java
// java.lang.OutOfMemoryError: Java heap space
List<byte[]> list = new ArrayList<>();
while (true) {
    list.add(new byte[1024 * 1024]);  // allocate 1MB forever
}

// Node.js
// FATAL ERROR: Reached heap limit — Allocation failed
  • Memory leak: objects accumulate and are never garbage-collected
  • Loading an enormous file or dataset into memory all at once
  • Creating too many objects in a tight loop without releasing them
  • Cache with no eviction policy growing unboundedly
In Java, you can increase heap size with `-Xmx` JVM flag. In Node.js with `--max-old-space-size`. But the real fix is to find and fix the memory leak.

RuntimeError

Runtime

A general catch-all error category raised when something goes wrong during execution that doesn't fit a more specific type.

At runtime. Mostly a Python term — other languages raise more specific exception types.

# Python
import sys
if sys.getrecursionlimit() == 0:
    raise RuntimeError("recursion limit is 0")

# Also raised when a generator is already closed:
def gen():
    yield 1
g = gen()
next(g)
g.close()
next(g)  # RuntimeError: generator already executing
  • Logic condition detected as invalid state
  • Using a resource after it has been closed
  • Version mismatch between packages
  • Reaching code that should be unreachable
When you see RuntimeError in Python, the message content is the key — the error name itself is not very specific.

TimeoutError

Network / I/O

A requested operation did not complete within the allowed time — the waiting period expired.

During network calls, database queries, or any timed operation that takes too long.

// fetch with AbortController timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

fetch('/api/data', { signal: controller.signal })
  .catch(err => {
    if (err.name === 'AbortError') {
      console.error('Request timed out after 5s');
    }
  });

// Python requests
requests.get('https://example.com', timeout=5)
  # requests.exceptions.Timeout
  • Slow database query with no index on a large table
  • Downstream service is overloaded or unreachable
  • Network latency spike (especially cross-region)
  • Deadlock in the database waiting for a lock to release
Always set timeouts on network calls and DB queries — without them, your threads can block forever during an outage, eventually exhausting the thread pool.

ConnectionRefused

Network / I/O

The attempt to connect to a server was explicitly rejected — no service is listening on the target port.

When your application tries to open a TCP connection to a host:port where nothing is running or accepting connections.

# Python
import socket
s = socket.create_connection(('localhost', 5432))
  # ConnectionRefusedError: [Errno 111] Connection refused

# Common Docker scenario:
# Your app container starts before the DB container is ready
# ERROR: connect ECONNREFUSED 127.0.0.1:5432
  • The target service has not started yet (common in Docker Compose startup ordering)
  • Typo in the host or port in your config
  • Firewall blocking the port
  • The service crashed and is not running
Different from a Timeout: ConnectionRefused is immediate — the OS actively rejects the connection. A Timeout means no response at all (host may be unreachable or packet dropped).

PermissionDenied

System

The process does not have the system-level permission to access a file, directory, or resource.

When trying to read, write, or execute a file/resource that the operating system has restricted.

# Linux / macOS
open('/etc/shadow', 'r')
  # PermissionError: [Errno 13] Permission denied: '/etc/shadow'

# Writing to a read-only file
open('/usr/local/bin/app', 'w')
  # [Errno 13] Permission denied

# Common in Docker: running as root vs. non-root user
# EACCES: permission denied '/app/data/output.json'
  • File owned by root, but app runs as an unprivileged user
  • Incorrect file permissions (`chmod 600` instead of `chmod 644`)
  • Running in a container without the right user/volume permissions
  • Missing IAM role or policy (cloud storage, AWS S3 etc.)
In cloud and container contexts, this error is extremely common. The principle of least privilege intentionally prevents processes from accessing things they shouldn't — tune the permissions rather than running everything as root.

LogicError

Logic

The code runs without crashing, but produces the wrong result — the algorithm itself is incorrect.

At runtime — no exception is thrown. Only caught by tests, incorrect output, or user reports.

// Calculating average — wrong result, no crash
function average(numbers) {
  let sum = 0;
  for (let i = 0; i <= numbers.length; i++) {  // bug: <= instead of <
    sum += numbers[i];
  }
  return sum / numbers.length;
}
// NaN returned because numbers[numbers.length] is undefined
  • Off-by-one error in a loop boundary
  • Wrong formula (e.g., dividing before multiplying, wrong operator)
  • Incorrect conditional (`>=` instead of `>`, `&&` instead of `||`)
  • Mutating shared state unexpectedly
Logic errors are the hardest to find — no crash, no red squiggles in the IDE. Only thorough tests catch them. This is why test-driven development advocates writing tests before code.

ValueError

Runtime

A value has the right type but an inappropriate or invalid value for the operation being performed.

At runtime, typically in Python, when a function receives an argument of the correct type but a nonsensical value.

# Python
int("not a number")
  # ValueError: invalid literal for int() with base 10: 'not a number'

list1, list2 = [1, 2, 3]
  # ValueError: too many values to unpack (expected 2)
  • Passing an unparsable string to int()/float()
  • Unpacking a sequence into the wrong number of variables
  • A value falling outside an expected range or set of choices
  • Malformed input reaching a function that assumes it is already validated
ValueError vs TypeError: TypeError means the wrong kind of thing was passed; ValueError means the right kind of thing but a bad value. Validate and sanitise input before it reaches business logic.

KeyError

Runtime

The code tries to access a dictionary/map key that doesn't exist.

At runtime, when indexing a dict (or similar mapping) with a key that was never set.

# Python
config = {"host": "localhost"}
config["port"]
  # KeyError: 'port'

# Safer alternative
config.get("port", 8080)  # returns 8080 instead of raising
  • Typo in a key name
  • Assuming an optional config/JSON field is always present
  • Reading a key before it has been set
  • API response schema changed and a field was removed
The equivalent in JavaScript is silently getting undefined instead of an exception — which is easier to miss and can propagate further before failing.

AttributeError

Runtime

The code accesses an attribute or method that doesn't exist on an object.

At runtime, when Python looks up a name on an object and cannot find it defined.

# Python
class User:
    def __init__(self, name):
        self.name = name

u = User("Alice")
print(u.email)
  # AttributeError: 'User' object has no attribute 'email'

None.upper()
  # AttributeError: 'NoneType' object has no attribute 'upper'
  • Typo in an attribute or method name
  • Calling a method on None (a function returned nothing where a value was expected)
  • Using an attribute added in a newer version of a library you have an older version of
  • Confusing an instance with its class (calling an instance method on the class itself)
Python's equivalent of JavaScript's "Cannot read properties of undefined" — nearly always traced back to an unexpected None/undefined value earlier in the call chain.

ZeroDivisionError

Runtime

The code attempts to divide a number by zero, which is mathematically undefined.

At runtime, the instant a division or modulo operation has a zero denominator.

# Python
average = total / count
  # ZeroDivisionError: division by zero  (when count == 0)

// JavaScript does NOT throw — it returns Infinity or NaN
console.log(5 / 0);    // Infinity
console.log(0 / 0);    // NaN
  • Computing an average over an empty collection
  • A denominator derived from user input or a query result that can legitimately be zero
  • Percentage calculations when the total is zero
  • Rate calculations (e.g. requests per second) with a zero time delta
Language behaviour differs sharply here: Python and Java raise/throw, while JavaScript silently produces Infinity/NaN — always guard the denominator explicitly rather than relying on a crash to catch it.

AssertionError

Logic

A sanity-check assertion in the code evaluated to false, signalling that an assumption the programmer relied on turned out to be untrue.

At runtime, whenever an assert statement (or a test framework's assertion) is evaluated and fails.

# Python
def process(items):
    assert len(items) > 0, "items must not be empty"
    return items[0]

process([])
  # AssertionError: items must not be empty

// JavaScript (Node assert module)
assert.strictEqual(2 + 2, 5);
  // AssertionError [ERR_ASSERTION]: 2 + 2 !== 5
  • A precondition the function assumed was never actually guaranteed by its callers
  • A test expectation not matching actual output
  • Defensive assertions catching a bug earlier than it would otherwise surface
  • Assertions left enabled in production accidentally raising on edge cases
Unlike most exceptions, AssertionErrors are intentionally placed by the developer — they document an assumption. Many languages let you strip assertions in production builds (Python's -O flag) once assumptions are verified.

SegmentationFault

System

The process tried to access a memory address it is not allowed to touch — the operating system kills it immediately.

At runtime, in low-level languages (C, C++, Rust unsafe blocks) when memory is accessed incorrectly.

// C
int *ptr = NULL;
*ptr = 42;
  // Segmentation fault (core dumped)

char buf[10];
buf[100] = 'x';  // out-of-bounds write — undefined behaviour, may segfault
  • Dereferencing a null or invalid pointer
  • Writing past the end of an array/buffer (buffer overflow)
  • Using memory after it has been freed (use-after-free)
  • Stack corruption from a very deep or runaway recursion
This is the systems-programming cousin of a NullPointerException, but far less safe: instead of a catchable exception, the OS terminates the whole process, and no in-process handler can prevent it. Memory-safe languages (Rust, Go, Java) are largely designed to eliminate this class of bug.

DeadlockError

Runtime

Two or more threads/processes are each waiting for a resource the other holds — none of them can ever proceed.

At runtime, under concurrent execution, usually when locks are acquired in inconsistent order across threads.

// Pseudocode: classic deadlock
// Thread A:                    // Thread B:
lock(mutexX);                   lock(mutexY);
lock(mutexY);  // waits for B   lock(mutexX);  // waits for A
// Neither thread can ever acquire its second lock — frozen forever
  • Acquiring multiple locks in a different order in different threads
  • A thread holding a lock while waiting on I/O or another lock indefinitely
  • Circular wait between database transactions holding row/table locks
  • Nested locking without a consistent global lock ordering
Databases can usually detect and break deadlocks automatically (aborting one transaction), but in-process thread deadlocks in most languages just hang forever — always acquire locks in a fixed, documented order to avoid this entirely.

CORSError

Network / I/O

The browser blocked a cross-origin request because the server's response didn't include the required CORS headers.

In browser JavaScript, when a fetch/XHR request targets a different origin (domain, port, or protocol) than the page itself.

// Browser console
fetch('https://api.other-site.com/data')
  .catch(err => console.error(err));
// Access to fetch at 'https://api.other-site.com/data' from origin
// 'https://myapp.com' has been blocked by CORS policy: No
// 'Access-Control-Allow-Origin' header is present on the requested resource.
  • The API server does not send Access-Control-Allow-Origin for your origin
  • A preflight OPTIONS request is not handled correctly by the server
  • Credentials (cookies) sent cross-origin without Access-Control-Allow-Credentials
  • Calling an API directly from the browser that was only designed for server-to-server use
CORS is enforced entirely by the browser as a security boundary — the request often still reaches the server and can succeed there (e.g. in Postman or curl); only the browser blocks reading the response in your page's JavaScript.

Quick Reference

Error name Category Plain meaning Language
SyntaxErrorCompile-timeCode grammar is wrong — cannot be parsedJS, Python, most
TypeErrorRuntimeWrong type used in an operationJS, Python
ReferenceErrorRuntimeVariable or name not found in scopeJS
NullPointerExceptionRuntimeUsed a reference that points to nullJava, Kotlin, C#
IndexOutOfBoundsExceptionRuntimeArray/list index does not existJava; IndexError in Python
StackOverflowErrorRuntimeCall stack exhausted (infinite recursion)Java; RangeError in JS
OutOfMemoryErrorRuntimeNo heap memory left to allocateJava; fatal in Node.js
RuntimeErrorRuntimeGeneral execution error (no crash name)Python
TimeoutErrorNetwork / I/OOperation took too longAll (varies by library)
ConnectionRefusedNetwork / I/ONothing is listening on that portOS-level, all languages
PermissionDeniedSystemOS rejected the file/resource accessOS-level, all languages
Logic ErrorLogicWrong output, no exception thrownAll (caught by tests only)
ValueErrorRuntimeRight type, invalid/nonsensical valuePython
KeyErrorRuntimeDict/map key does not existPython
AttributeErrorRuntimeAttribute or method does not existPython
ZeroDivisionErrorRuntimeDivision by zeroPython, Java; Infinity/NaN in JS
AssertionErrorLogicAn assert statement evaluated falsePython, JS, most test frameworks
SegmentationFaultSystemIllegal memory access — process killed by OSC, C++, unsafe Rust
DeadlockErrorRuntimeThreads waiting on each other foreverAll languages with locks/threads
CORSErrorNetwork / I/OBrowser blocked a cross-origin requestBrowser JavaScript

Frequently Asked Questions

What's the difference between a TypeError and a ReferenceError?

A ReferenceError means the code refers to a name that has never been declared or is out of scope — the engine cannot find it at all, e.g. `console.log(userName)` when `userName` was never defined. A TypeError means the name resolves to something, but you tried to use that value in a way its type does not support, like calling a number as a function or reading a property off `null`.

What's the difference between a TimeoutError and ConnectionRefused?

They fail at different stages of a network call. ConnectionRefused is immediate — the operating system actively rejects the connection because nothing is listening on that host:port. A TimeoutError means the connection attempt (or the request itself) simply never got a response in time, which usually points to an unreachable host, a dropped packet, or an overloaded downstream service rather than a rejected connection.

Why does a SyntaxError stop the whole program before it even runs, but a TypeError does not?

SyntaxError is caught at parse time — the interpreter or compiler cannot understand the code at all, so the program never starts. TypeError is a runtime error: the code is grammatically valid and starts executing, but the crash happens later, the moment an operation is attempted on a value of the wrong type.