Error Philosophy Timeline

How Python and LLM frameworks evolved their approach to error handling — from bare exceptions to typed validation.

Click any event to expand details.

2000 — Python 2.0
Bare exceptions — the wild west
Python's exception system exists but no conventions. Most libraries raise generic Exception or RuntimeError with terse messages.
No standardGeneric errors Typical error of the era:
Exception: something went wrong
No guidance on what went wrong, where, or how to fix it. Debugging required reading source code.
2008 — Django 1.0
Framework errors get contextual
Django's debug page shows the full request context, template line, and variable state alongside the traceback. A step change in developer experience.
ContextualActionable Django established the idea that an error should tell you not just what failed but where in your code to look. The yellow debug page became the gold standard for web framework errors.
2012 — Requests library
HTTP errors become readable
Kenneth Reitz's Requests wraps urllib's cryptic errors into human-readable ConnectionError, HTTPError, Timeout. "Errors should never pass silently."
Named exceptionsClear hierarchy Before Requests: socket.error: [Errno 111] Connection refused
After Requests: requests.exceptions.ConnectionError: Failed to establish a new connection

The lesson: wrap internal errors in domain-specific exceptions with plain-language messages.
2019 — Pydantic v1
Validation errors become structured
Pydantic introduces ValidationError with field-level error messages, expected vs actual type, and location in the model. The standard for Python data validation.
Field-levelExpected vs actualEager
pydantic.ValidationError: 1 validation error for User email value is not a valid email address (type=value_error.email)
This is what good validation errors look like: which field, what was wrong, what was expected.
2022 — LangChain 0.0.1
LLM frameworks ship — validation mixed
LangChain launches with strong missing-key errors but lazy model validation. The abstraction leaks in wrong-type errors: 'str' object has no attribute 'page_content'.
Good: API key errorsBad: wrong-type errorsLazy: model validation Best error:
openai.OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable
Worst error:
AttributeError: 'str' object has no attribute 'page_content'
2022 — LlamaIndex 0.1
Silent failure on missing credentials
LlamaIndex's OpenAI() accepts no required args. Missing API key raises no error at init. Failure surfaces only on the first query — after full indexing.
Silent failureFully lazy Pattern:
from llama_index.llms.openai import OpenAI llm = OpenAI() # No error — key is missing Settings.llm = llm # No error index = VectorStoreIndex... # Indexes all 10,000 docs response = index.query(...) # AuthenticationError HERE
The indexing work is wasted. You must fix the config and re-run.
2023 — SynapseKit 0.1
Provider enumeration in errors
SynapseKit's unified provider interface enables one useful pattern: listing all valid providers directly in the error message when an invalid one is passed.
Provider list in errorEager on providerLazy on API key
ValueError: Unknown provider: 'fakeai'. Valid providers: 'openai', 'anthropic', 'ollama', 'groq', 'mistral', 'gemini', 'deepseek', 'cohere', 'azure', 'bedrock', 'together', 'anyscale', 'perplexity', 'fireworks'
This is the best single error message in the benchmark — no docs needed.
2026 — Where we are today
The unsolved problem: wrong-type errors
All three frameworks still produce opaque AttributeError tracebacks for wrong-type inputs. A solved problem (one isinstance check) that none apply consistently.
All three fail hereFixable in 1 line What all three give you:
AttributeError: 'str' object has no attribute 'page_content' # or AttributeError: 'str' object has no attribute 'id_'
What one isinstance check gives you:
TypeError: expected list[Document], got str. Use: index.from_documents([Document(text="...")])
www.engineersofai.com · AI Letters #15