Why Logging Just the Error Object in JavaScript Is Not Enough
JavaScript Error Logging: Beyond the Object — Why You Need More
In JavaScript development, whether you’re writing for Node.js, the browser, or any JS runtime, error handling is critical. A common but flawed practice is this:
try {
riskyOperation();
} catch (err) {
console.log(err);
}
It seems fine at first glance. But if you’re working with modern APIs, custom errors, or third-party libraries, this simple console.log(err) often hides valuable debugging information.
> Just printing the error object isn’t enough — you might be missing custom properties, status codes, nested metadata, and even Symbol-based information.
## ❌ The Problem: `console.log(err)` is too shallow
Consider this example using the `fetch` API:
async function getData() {
try {
const response = await fetch("https://nonexistent.domain/test");
const data = await response.json();
} catch (err) {
console.log(err);
}
}
getData();
### Output:
What’s missing?
* No info about the URL
* No HTTP response data (status, headers)
* No `name`, `code`, or potential nested fields if the error was wrapped
* No err.response.headers
Let’s go deeper with another case: a custom error object.
Custom JavaScript error:
class AppError extends Error {
constructor(message, code, meta) {
super(message);
this.name = "AppError";
this.code = code;
this.meta = meta;
}
}
try {
throw new AppError("Database failed", "DB_CONN", { retry: true });
} catch (err) {
console.log(err);
}
Output:
What you don’t see:
* `code: “DB_CONN”`
* `meta: { retry: true }`
These are critical for debugging and recovery logic — and are silently ignored unless you extract them manually.
✅ The Solution: Introspect All Error Properties
Use `Reflect.ownKeys` to reveal all fields even symbols
function logAllErrorDetails(err) {
const keys = Reflect.ownKeys(err);
for (const key of keys) {
console.log(`${String(key)}:`, err[key]);
}
}
✅ Reveals:
* Custom fields like `code`, `meta`, `status`
* Hidden or non-enumerable props
* `Symbol(…)` properties