| name | nomistakes |
| description | Error prevention and best practices enforcement for agent-assisted coding. Use when writing code to catch common mistakes, enforce patterns, prevent bugs, validate inputs, handle errors, follow coding standards, avoid anti-patterns, and ensure code quality through proactive checks and guardrails. |
No Mistakes: Error Prevention & Best Practices Enforcement
Purpose
This skill helps agents write higher-quality code by proactively preventing common errors, enforcing best practices, and applying defensive programming patterns. Use this skill when writing any code to reduce bugs, improve maintainability, and follow established patterns.
When to Use This Skill
Activate this skill when:
- Writing new code or functions
- Refactoring existing code
- Implementing API integrations
- Handling user input or external data
- Working with async operations
- Managing state or side effects
- Writing tests or validation logic
- Reviewing code for potential issues
Core Error Prevention Principles
1. Input Validation (Guard at Boundaries)
Always validate inputs at function boundaries:
function processUser(id: string) {
return database.query(id);
}
function processUser(id: string) {
if (!id || typeof id !== 'string') {
throw new Error('Invalid user ID: must be non-empty string');
}
if (!/^[a-zA-Z0-9-]+$/.test(id)) {
throw new Error('Invalid user ID format: alphanumeric and hyphens only');
}
return database.query(id);
}
Validation checklist:
2. Error Handling (Fail Fast, Fail Loudly)
Never silently swallow errors:
try {
await criticalOperation();
} catch (e) {
}
try {
await criticalOperation();
} catch (e) {
logger.error('Critical operation failed', { error: e, context });
throw new ApplicationError('Operation failed', { cause: e });
}
Error handling checklist:
3. Null Safety (Avoid Billion Dollar Mistakes)
Treat null/undefined as exceptional:
function getUserName(user) {
return user.profile.name;
}
function getUserName(user: User | null): string {
if (!user?.profile?.name) {
return 'Unknown User';
}
return user.profile.name;
}
Null safety checklist:
4. Async Operations (No Race Conditions)
Handle async errors and edge cases:
async function loadData() {
const data = await fetch(url);
return data;
}
async function loadData(): Promise<Result<Data>> {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
if (!response.ok) {
return { error: `HTTP ${response.status}` };
}
const data = await response.json();
return { data };
} catch (e) {
if (e.name === 'AbortError') {
return { error: 'Request timeout' };
}
return { error: `Network error: ${e.message}` };
}
}
Async safety checklist:
5. Type Safety (Make Invalid States Unrepresentable)
Use TypeScript to prevent bugs at compile time:
type Status = string;
function setStatus(status: Status) { ... }
setStatus("complted");
type Status = 'pending' | 'completed' | 'failed';
function setStatus(status: Status) { ... }
setStatus("complted");
type State =
| { status: 'idle' }
| { status: 'loading'; startedAt: number }
| { status: 'success'; data: Data }
| { status: 'error'; error: Error };
Type safety checklist:
6. Boundary Checks (Arrays, Strings, Numbers)
Always validate indices and ranges:
function getItem(index: number) {
return items[index];
}
function getItem(index: number): Item {
if (index < 0 || index >= items.length) {
throw new RangeError(`Index ${index} out of bounds [0, ${items.length})`);
}
return items[index];
}
Boundary checklist:
7. Resource Management (Clean Up After Yourself)
Always release resources:
async function processFile(path: string) {
const file = await fs.open(path);
const data = await file.read();
return data;
}
async function processFile(path: string) {
const file = await fs.open(path);
try {
const data = await file.read();
return data;
} finally {
await file.close();
}
}
await using file = await fs.open(path);
const data = await file.read();
return data;
Resource checklist:
8. Immutability (Avoid Mutation Bugs)
Prefer immutable operations:
function addItem(list: Item[], item: Item) {
list.push(item);
return list;
}
function addItem(list: Item[], item: Item): Item[] {
return [...list, item];
}
function updateUser(user: User, name: string): User {
return { ...user, name };
}
Immutability checklist:
9. Configuration Validation (Fail at Startup)
Validate configuration early:
function sendEmail(to: string) {
const apiKey = process.env.SENDGRID_API_KEY;
return sendgrid.send({ to, apiKey });
}
const config = {
sendgridApiKey: requireEnv('SENDGRID_API_KEY'),
databaseUrl: requireEnv('DATABASE_URL'),
};
function requireEnv(key: string): string {
const value = process.env[key];
if (!value) {
throw new Error(`Missing required env var: ${key}`);
}
return value;
}
Configuration checklist:
10. Defensive Programming (Assume the Worst)
Code as if everything can fail:
function parseJSON(text: string) {
return JSON.parse(text);
}
function parseJSON(text: string): Result<any> {
try {
if (!text || text.trim() === '') {
return { error: 'Empty JSON string' };
}
const data = JSON.parse(text);
return { data };
} catch (e) {
return { error: `Invalid JSON: ${e.message}` };
}
}
Defensive checklist:
11. Performance Anti-Patterns (Core Web Vitals)
Optimize for user experience metrics:
| Metric | Target | What it Measures |
|---|
| LCP (Largest Contentful Paint) | < 2.5s | Loading performance |
| INP (Interaction to Next Paint) | < 200ms | Interactivity/responsiveness |
| CLS (Cumulative Layout Shift) | < 0.1 | Visual stability |
function ProductPage() {
return (
<div>
{/* No dimensions = CLS issues */}
<img src={product.image} />
{/* Sync heavy computation = INP issues */}
<div>{expensiveCalculation(data)}</div>
{/* Render-blocking = LCP issues */}
<script src="huge-library.js" />
</div>
);
}
function ProductPage() {
const [result, setResult] = useState<Result | null>(null);
useEffect(() => {
requestIdleCallback(() => {
setResult(expensiveCalculation(data));
});
}, [data]);
return (
<div>
{/* Explicit dimensions prevent layout shift */}
<img
src={product.image}
width={400}
height={300}
loading="lazy"
decoding="async"
/>
{/* Skeleton prevents CLS while loading */}
{result ? <div>{result}</div> : <Skeleton />}
</div>
);
}
Performance checklist:
12. Fire-and-Forget Detection (Critical Bug Pattern)
Never use void with async functions in workers or handlers:
export async function handler() {
void processInBackground(data);
return { status: 'accepted' };
}
process.on('SIGTERM', () => {
void gracefulShutdown();
});
export async function handler() {
await processInBackground(data);
return { status: 'processed' };
}
export async function handler() {
processInBackground(data).catch(err => {
logger.error('Background task failed', { error: err });
metrics.increment('background_task_failures');
});
return { status: 'accepted' };
}
let shutdownPromise: Promise<void> | null = null;
process.on('SIGTERM', () => {
shutdownPromise = gracefulShutdown();
});
process.on('beforeExit', async () => {
if (shutdownPromise) await shutdownPromise;
});
ESLint rule to catch this:
{
"rules": {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error"
}
}
Fire-and-forget checklist:
13. Server Actions Error Handling (Next.js/React)
Never throw in Server Actions - return typed errors instead:
'use server'
export async function createUser(formData: FormData) {
const data = Object.fromEntries(formData);
if (!data.email) {
throw new Error('Email required');
}
const user = await db.users.create(data);
return user;
}
'use server'
export async function createUser(formData: FormData): Promise<
{ data: User } | { error: string }
> {
const raw = Object.fromEntries(formData);
const parsed = UserSchema.safeParse(raw);
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors };
}
try {
const user = await db.users.create(parsed.data);
return { data: user };
} catch (e) {
logger.error('Failed to create user', { error: e });
return { error: 'Failed to create user. Please try again.' };
}
}
function CreateUserForm() {
const [state, formAction] = useActionState(createUser, null);
return (
<form action={formAction}>
{state?.error && <ErrorMessage>{state.error}</ErrorMessage>}
<input name="email" type="email" required />
<button type="submit">Create</button>
</form>
);
}
Server Actions checklist:
14. Security Quick Wins (OWASP Top 10 2025)
Essential security patterns for every application:
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: { error: 'Too many login attempts. Try again later.' },
standardHeaders: true,
});
app.post('/login', loginLimiter, loginHandler);
import { z } from 'zod';
const UserInputSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s-]+$/),
});
import { encode } from 'html-entities';
function renderUserContent(content: string): string {
return encode(content);
}
const query = `SELECT * FROM users WHERE id = '${userId}'`;
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
const API_KEY = 'sk-1234567890abcdef';
const API_KEY = requireEnv('API_KEY');
Security headers (add to all responses):
app.use(helmet({
contentSecurityPolicy: true,
crossOriginEmbedderPolicy: true,
crossOriginOpenerPolicy: true,
crossOriginResourcePolicy: true,
dnsPrefetchControl: true,
frameguard: true,
hidePoweredBy: true,
hsts: true,
ieNoOpen: true,
noSniff: true,
referrerPolicy: true,
xssFilter: true,
}));
Security checklist:
15. TypeScript 5.x Safety Features
Leverage modern TypeScript for safer code:
function createRoute<T extends string[]>(paths: T) {
return paths;
}
const routes = createRoute(['users', 'posts']);
function createRoute<const T extends string[]>(paths: T) {
return paths;
}
const routes = createRoute(['users', 'posts']);
const config: Config = { port: 3000 };
const config = { port: 3000 } satisfies Config;
async function processFile(path: string) {
const file = await fs.open(path);
try {
return await file.read();
} finally {
await file.close();
}
}
async function processFile(path: string) {
await using file = await fs.open(path);
return await file.read();
}
function createFSM<S extends string>(
initial: NoInfer<S>,
states: S[]
) { }
import config from './config.json' with { type: 'json' };
TypeScript 5.x checklist:
16. Modern Validation Library Selection
Choose the right validation library for your use case:
| Library | Bundle Size | Best For |
|---|
| Zod | ~14KB gzip | Server-side, complex schemas, transforms |
| Valibot | ~600B gzip | Client-side, bundle-critical, simple schemas |
| @effect/schema | Varies | Type-safe errors, branded types, enterprise |
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email(),
age: z.number().min(0).max(150),
role: z.enum(['admin', 'user']),
}).transform(data => ({
...data,
email: data.email.toLowerCase(),
}));
import * as v from 'valibot';
const UserSchema = v.object({
email: v.pipe(v.string(), v.email()),
age: v.pipe(v.number(), v.minValue(0), v.maxValue(150)),
role: v.picklist(['admin', 'user']),
});
Validation library checklist:
Common Anti-Patterns to Avoid
1. The Silent Failure
try {
await importantOperation();
} catch (e) {
}
Fix: Always log, re-throw, or return error.
2. The String Error
throw "Something went wrong";
throw new Error("Something went wrong");
3. The Floating Promise
async function handler() {
someAsyncOperation();
}
async function handler() {
void someAsyncOperation().catch(logError);
await someAsyncOperation();
}
4. The Type Assertion Lie
const user = apiResponse as User;
const user = UserSchema.parse(apiResponse);
5. The Magic Number
if (status === 2) { ... }
const STATUS_COMPLETED = 2;
if (status === STATUS_COMPLETED) { ... }
enum Status { Pending = 1, Completed = 2 }
if (status === Status.Completed) { ... }
Testing Best Practices
Test Error Cases First
describe('processPayment', () => {
it('throws on invalid amount', () => {
expect(() => processPayment(-10)).toThrow('Invalid amount');
});
it('throws on missing payment method', () => {
expect(() => processPayment(10, null)).toThrow('Payment method required');
});
it('processes valid payment', () => {
const result = processPayment(10, { type: 'card' });
expect(result.success).toBe(true);
});
});
Use Property-Based Testing for Edge Cases
test('parseAmount never returns negative', () => {
fc.assert(fc.property(fc.string(), (input) => {
const result = parseAmount(input);
return result === null || result >= 0;
}));
});
Pre-Commit Checklist
Before completing your task, verify:
Core Safety
Modern Patterns (2024+)
Security
Quality
Quick Reference: Error Prevention Patterns
| Scenario | Pattern |
|---|
| External API call | Try/catch + timeout + retry logic |
| User input | Validate with schema (Zod, Joi) |
| Array access | Check length before index |
| Object property | Use optional chaining ?. |
| Async operation | Always await or .catch() |
| Configuration | Validate at startup |
| Type unknown | Use unknown + type guards |
| Resource (file/socket) | Use using (TS 5.2+) or finally |
| State machine | Discriminated unions |
| Error context | Include error cause chain |
| Background job | Never void asyncFn(), always await |
| Server Action | Return {error} or {data}, never throw |
| Client validation | Valibot (600B) over Zod (14KB) |
| Image rendering | Explicit width/height, lazy loading |
| Heavy computation | Web Worker or requestIdleCallback |
| Auth endpoint | Rate limiting middleware |
| User content | Escape HTML/JS before render |
| Literal types | Use const type params (TS 5.0+) |
| Config objects | Use satisfies (TS 4.9+) |
Further Reading
For detailed examples and reference implementations, see:
references/error-handling-patterns.md - Comprehensive error handling guide
references/typescript-safety.md - Advanced TypeScript safety patterns
references/testing-strategies.md - Test coverage for error conditions
references/ai-review-checklist.md - Code review checklist for AI agents
references/adversarial-review.md - VDD pattern for hostile code review
references/api-reference.md - Quick lookup for patterns and code examples
references/real-world-patterns.md - Real-world implementation examples
references/BIOME_MIGRATION.md - ESLint/Prettier to Biome migration guide
When NOT to Use This Skill
- Writing proof-of-concept code (but refactor before production)
- Performance-critical hot paths (after profiling shows overhead)
- Throwaway scripts (but consider: will this become production code?)