Adding information to an exception?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When an exception reaches a log or an error tracker, the original message is often too vague to diagnose the problem quickly. Adding context to an exception is therefore a good practice, but only if you do it in a way that preserves the original error and stack trace. The goal is not to replace the exception with a prettier sentence. The goal is to attach useful state such as input values, operation names, and failure context without hiding the real cause.
Prefer Wrapping Over Replacing
A common mistake is catching an exception and throwing a brand-new one with only a custom message. That destroys the original context unless you chain the original exception as the cause.
Bad pattern:
Better pattern:
The second version adds the missing domain detail while still preserving the original exception chain.
Python: Add Context Without Losing the Cause
Python gives you several practical options.
For one-off wrapping, raise ... from exc is the cleanest pattern.
If you need a domain-specific error, define a custom exception type:
This keeps the higher-level code expressive without discarding the low-level cause.
C#: Preserve the Inner Exception
C# has the same design rule: if you add a new exception message, include the original exception as InnerException.
Without the ex argument in the constructor, the original failure details are much harder to recover.
Add Context Fields, Not Just Longer Messages
A useful exception message usually answers at least one of these questions:
- what operation was happening
- which resource or identifier was involved
- what input or state made the failure relevant
That means these are often good additions:
- file path
- user ID
- remote endpoint
- SQL query name, not raw credentials
- batch number or job ID
You want enough detail to narrow the problem fast, but not so much that the exception becomes a dump of unfiltered state.
Be Careful With Sensitive Data
More context is not always better. Never attach secrets, personal data, or tokens casually just because they might be useful during debugging.
Examples of risky values:
- passwords
- full access tokens
- raw customer PII
- connection strings with credentials
A good pattern is to include identifiers and safe metadata while redacting sensitive payloads.
That keeps the message actionable without leaking secrets.
Logging and Exceptions Are Different Tools
Another important design point: not all diagnostic detail belongs in the exception message itself. Some context belongs in structured logs around the exception.
For example, you may log:
- request ID
- trace ID
- sanitized payload size
- environment and service name
Then raise or rethrow an exception with a clean, focused message. This balance keeps exception messages readable while still giving operators enough surrounding telemetry.
When Not to Wrap
Do not wrap every exception reflexively. If the current exception type and message already describe the failure well, catching and rethrowing just adds noise.
For example, if a public API already raises a meaningful FileNotFoundError at the right abstraction level, wrapping it in a generic RuntimeError may make things worse rather than better.
Wrap when you are crossing an abstraction boundary and need to explain why a low-level failure matters in your domain.
Common Pitfalls
The biggest mistake is replacing the original exception with a custom one and dropping the cause. That destroys the debugging path.
Another mistake is stuffing huge payloads into exception messages. That makes logs noisy and can expose sensitive data.
Developers also often add context at the wrong layer. If every low-level helper wraps everything, the stack becomes cluttered with repetitive messages.
Finally, do not rely only on exception text when structured logging can carry richer, safer metadata alongside the error.
Summary
- Add information to exceptions when it helps explain the failure in domain terms.
- Preserve the original exception with chaining in Python or
InnerExceptionin C#. - Include useful identifiers and operation context, not uncontrolled dumps of state.
- Keep secrets and sensitive data out of exception messages.
- Wrap selectively at abstraction boundaries instead of rethrowing everything blindly.

