Python vs JavaScript Error Handling
Exception handling, custom error types, and resource cleanup in Python versus JavaScript.
try/except vs try/catch
The basic structure is the same: wrap risky code in a try block, handle errors in a catch (JS) or except (Python) block, and optionally run cleanup in finally. The keyword difference -- except vs catch -- is cosmetic, but the binding syntax is not. Python names the caught exception with as: except ValueError as e. JavaScript uses a parenthesized binding: catch (e). Python also lets you catch multiple exception types in a single clause: except (ValueError, TypeError) as e. JavaScript catches everything in one clause and relies on instanceof checks to differentiate. This means Python's error handling is more granular at the syntax level -- you can stack multiple except clauses for different types without nested ifs. JavaScript developers compensate with guard patterns inside catch, but the result is often less readable. Another Python-specific feature: bare except (without a type) catches everything including SystemExit and KeyboardInterrupt, which is almost always a mistake. The Python community strongly discourages bare except in favor of except Exception. JavaScript's catch always catches everything (there is no type filtering), so you must filter manually.
Basic error handling
try:
result = int("not a number")
except ValueError as e:
print(f"Failed: {e}")
finally:
print("cleanup")try {
const result = Number.parseInt("not a number");
if (Number.isNaN(result)) throw new Error("NaN");
} catch (e) {
console.log(`Failed: ${e.message}`);
} finally {
console.log("cleanup");
}Multiple exception types
try:
data = fetch_data()
except (ConnectionError, TimeoutError) as e:
handle_network_error(e)
except ValueError:
handle_bad_data()try {
const data = fetchData();
} catch (e) {
if (e instanceof TypeError) {
handleNetworkError(e);
} else if (e instanceof RangeError) {
handleBadData();
} else { throw e; }
}Can you write this from memory?
Write a try/except skeleton that catches Exception. Use pass in both blocks.
Custom Exceptions and Error Classes
Python custom exceptions inherit from Exception (or a more specific built-in). The convention is a class with a descriptive name and optionally an __init__ that accepts context: class InsufficientFundsError(Exception): def __init__(self, balance): .... JavaScript custom errors extend Error and should set the name property so that stack traces identify them correctly. Both patterns are straightforward, but the ecosystem conventions differ. Python has a rich hierarchy: ValueError, TypeError, KeyError, IndexError, IOError, each with defined semantics. Libraries are expected to raise specific built-in types where appropriate and define custom exceptions for domain-specific failures. JavaScript's built-in hierarchy is flatter: Error, TypeError, RangeError, SyntaxError, ReferenceError. Libraries often throw plain Error with a message string, or use a code property (err.code === "ENOENT"). The result is that Python error handling tends to be more type-driven (catch specific classes), while JavaScript error handling tends to be more value-driven (inspect properties). Neither approach is inherently better, but switching between them requires adjusting your mental model of what a "kind of error" means.
Custom exception / error class
class InsufficientFunds(Exception):
def __init__(self, balance):
self.balance = balance
super().__init__(
f"Balance too low: {balance}"
)
raise InsufficientFunds(42)class InsufficientFunds extends Error {
constructor(balance) {
super(`Balance too low: ${balance}`);
this.name = "InsufficientFunds";
this.balance = balance;
}
}
throw new InsufficientFunds(42);Can you write this from memory?
Write a try/except skeleton that catches Exception. Use pass in both blocks.
Context Managers vs try/finally
Python's with statement wraps resource acquisition and release into a protocol (__enter__ and __exit__). Opening a file with with open("f.txt") as f: guarantees the file is closed when the block exits, even if an exception fires. JavaScript has no built-in equivalent. The closest patterns are try/finally for synchronous cleanup and the using keyword from the Explicit Resource Management proposal (stage 3, using const handle = ...). Until that proposal ships universally, JavaScript developers write try { ... } finally { resource.close() } or lean on callback-based APIs (fs.readFile). The gap is significant for Python developers who rely on context managers daily -- database connections, locks, temporary directories, HTTP sessions. Python's contextlib module even provides @contextmanager to create custom context managers from generator functions, a pattern that compresses setup/teardown into a few lines. JavaScript's pattern of wrapping cleanup in finally blocks works but scales poorly: nesting three resources means three try/finally levels, while Python handles it with a single with line using the comma syntax (with open(a) as fa, open(b) as fb:). The using proposal will close this gap, but as of early 2026 it remains unavailable in most runtimes without transpilation.
File resource cleanup
with open("data.txt") as f:
content = f.read()
# file auto-closed hereimport { readFile } from "node:fs/promises";
// no built-in "with" equivalent
const content = await readFile(
"data.txt", "utf-8"
);
// GC handles file descriptor eventuallyCustom context manager vs try/finally
from contextlib import contextmanager
@contextmanager
def timer():
start = time.time()
yield
print(f"Elapsed: {time.time() - start:.2f}s")
with timer():
do_work()function withTimer(fn) {
const start = performance.now();
try {
return fn();
} finally {
const ms = performance.now() - start;
console.log(`Elapsed: ${(ms/1000).toFixed(2)}s`);
}
}
withTimer(() => doWork());Error Propagation Patterns
Both languages propagate exceptions up the call stack automatically -- if nothing catches an error, it bubbles to the top-level handler. The difference lies in async code. Python's async/await propagates exceptions through the coroutine chain exactly like synchronous code, so try/except works identically in async functions. JavaScript's async/await also propagates through the promise chain, but unhandled rejections have historically been silently swallowed (the "unhandled promise rejection" warning). Node.js changed the default to crash on unhandled rejections in v15, aligning closer to Python's behavior. Browser environments still vary. Python's error propagation is straightforward because the language has one execution model with cooperative concurrency bolted on via asyncio. JavaScript's event loop means errors can escape through callbacks, event handlers, setTimeout, and promise chains -- each requiring a different interception strategy (try/catch for sync, .catch() for promises, window.onerror for globals). This fragmentation is a real source of bugs: wrapping only the synchronous portion of an operation in try/catch misses async errors entirely. Python developers moving to JavaScript should adopt the habit of always awaiting promises inside try blocks rather than relying on .catch() callbacks.
Async error handling
import asyncio
async def fetch_data(url):
raise ConnectionError("timeout")
async def main():
try:
data = await fetch_data("/api")
except ConnectionError as e:
print(f"Network error: {e}")
asyncio.run(main())async function fetchData(url) {
throw new Error("timeout");
}
async function main() {
try {
const data = await fetchData("/api");
} catch (e) {
console.log(`Network error: ${e.message}`);
}
}
main();