error-tracing — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited error-tracing (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Read stack traces and error chains to trace errors back to their root cause across synchronous, asynchronous, and distributed code paths.
Stack traces read from top to bottom. The top frame is where the error was thrown; the bottom frame is the entry point:
Error: Cannot read properties of undefined (reading 'email')
at formatUser (src/utils/format.ts:42:18) <-- ERROR THROWN HERE
at processUsers (src/services/user.ts:87:12) <-- Called formatUser
at handler (src/routes/users.ts:23:5) <-- Called processUsers
at Layer.handle (node_modules/express/lib/router/layer.js:95:5)Reading strategy:
node_modules)src/utils/format.ts:42undefined and trace where it came from// The error says: Cannot read 'email' of undefined
// At format.ts:42: user.email — so `user` is undefined
// At user.ts:87: formatUser(users[i]) — so users[i] is undefined
// Root cause: array has gaps or index is out of boundsWrap errors to add context while preserving the original cause:
// Build error chains with the cause option (ES2022)
async function createOrder(data: OrderInput): Promise<Order> {
let user: User;
try {
user = await fetchUser(data.userId);
} catch (error) {
throw new Error(`Failed to create order: user lookup failed`, {
cause: error,
});
}
try {
return await insertOrder(user, data);
} catch (error) {
throw new Error(`Failed to create order: database insert failed`, {
cause: error,
});
}
}
// Reading the error chain
function logErrorChain(error: Error): void {
let current: Error | undefined = error;
let depth = 0;
while (current) {
const indent = " ".repeat(depth);
console.error(`${indent}${current.message}`);
if (current.stack) {
console.error(`${indent}${current.stack.split("\n")[1]?.trim()}`);
}
current = current.cause instanceof Error ? current.cause : undefined;
depth++;
}
}
// Output:
// Failed to create order: database insert failed
// at createOrder (src/services/order.ts:18:11)
// ECONNREFUSED 127.0.0.1:5432
// at Connection.connect (node_modules/pg/lib/connection.js:45:9)# Python: Exception chaining with raise ... from
def create_order(data: dict) -> Order:
try:
user = fetch_user(data["user_id"])
except DatabaseError as e:
raise OrderError(f"Failed to create order for user {data['user_id']}") from e
# The __cause__ attribute preserves the original exceptionAsync operations can lose stack context. Use these techniques to preserve traceability:
// Node.js: Enable async stack traces
// Run with: node --async-stack-traces app.js
// Or set in code:
Error.stackTraceLimit = 50; // Increase from default 10
// PROBLEM: Lost context in async callbacks
setTimeout(() => {
throw new Error("What called setTimeout?");
// Stack trace only shows the timer callback, not the caller
}, 1000);
// SOLUTION: Capture context before the async boundary
async function processWithContext(items: Item[]): Promise<void> {
const callerStack = new Error("context").stack; // Capture before async
await Promise.all(
items.map(async (item) => {
try {
await processItem(item);
} catch (error) {
throw new Error(
`Failed processing item ${item.id}. Caller: ${callerStack}`,
{ cause: error }
);
}
})
);
}
// SOLUTION: Named functions instead of anonymous callbacks
// BAD: Anonymous — shows as "<anonymous>" in stack
items.map(async (item) => { /* ... */ });
// GOOD: Named — shows as "processItem" in stack
items.map(async function processItem(item) { /* ... */ });Catch rendering errors and prevent full-app crashes:
import { Component, ErrorInfo, ReactNode } from "react";
interface ErrorBoundaryProps {
fallback: ReactNode;
children: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
state: ErrorBoundaryState = { hasError: false, error: null };
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// Log the error with component stack
console.error("Component error:", {
message: error.message,
componentStack: errorInfo.componentStack,
stack: error.stack,
});
this.props.onError?.(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
// Usage: Wrap sections of the UI independently
function App() {
return (
<div>
<ErrorBoundary fallback={<p>Navigation error</p>}>
<Navigation />
</ErrorBoundary>
<ErrorBoundary fallback={<p>Content failed to load</p>}>
<MainContent />
</ErrorBoundary>
</div>
);
}Map minified stack traces back to original source:
# Generate source maps during build
# webpack: devtool: "source-map"
# vite: build.sourcemap: true
# tsc: "sourceMap": true in tsconfig.json
# Decode a minified stack trace
npx source-map-cli resolve bundle.js.map 1 2345
# Output: src/services/order.ts:42:18
# Upload source maps to error tracking service
npx sentry-cli releases files v1.0.0 upload-sourcemaps ./dist// In error tracking setup (Sentry example)
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
release: process.env.APP_VERSION,
environment: process.env.NODE_ENV,
});
// Errors automatically include deminified stack tracescatch (e) {} — silent failures hide bugs. Always log or re-throw.throw new Error("failed") without { cause: originalError } — the root cause is lost.throw "something failed" — always throw Error objects for stack traces.// BAD: Swallowed error
try {
await saveOrder(order);
} catch (e) {
// Silently fails — order is lost, user thinks it succeeded
}
// BAD: Original cause lost
try {
await saveOrder(order);
} catch (e) {
throw new Error("Order save failed"); // What was the actual error?
}
// GOOD: Error chained with context
try {
await saveOrder(order);
} catch (error) {
throw new Error(`Order ${order.id} save failed`, { cause: error });
}Reading Stack Traces:
1. Read the error message first
2. Find the first frame in YOUR code (skip node_modules)
3. Go to that file:line
4. Trace the undefined/null value up the call stack
Error Chaining:
throw new Error("context message", { cause: originalError })
Python: raise NewError("message") from original_error
Async Debugging:
- node --async-stack-traces
- Error.stackTraceLimit = 50
- Capture caller stack before async boundaries
- Use named functions, not anonymous callbacks
React Error Boundaries:
- Catch rendering errors per UI section
- Log componentStack from ErrorInfo
- Show fallback UI, not a white screen
Production Errors:
- Generate source maps at build time
- Upload to error tracking (Sentry, Datadog)
- Never expose source maps publicly
- Include release version in error reports
Rules:
- Never swallow errors silently
- Always chain errors with cause
- Always throw Error objects, not strings
- Wrap specific operations, not entire functions~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.