Debugging Common AI Mistakes
Content
Professional documentation and insights
Debugging Common AI Mistakes
AI code generation tools are powerful, but they make predictable mistakes. Learn how to quickly identify and debug the most common issues in ChatGPT, Copilot, and Claude-generated code.
The AI Debugging Mindset
AI tools don’t understand context the way humans do. They pattern-match from training data, which means:
- They copy outdated practices from old Stack Overflow posts
- They assume happy paths and ignore edge cases
- They prioritize “working code” over maintainable code
- They miss project-specific requirements you haven’t explicitly stated
This guide helps you spot and fix these patterns before they cause production issues.
1. The “It Works on My Machine” Bug
Symptom
Code runs perfectly in development but crashes in production or on different machines.
Why AI Does This
AI doesn’t know your deployment environment, so it uses hardcoded paths, local dependencies, or environment-specific assumptions.
Common Examples
// ❌ AI-generated code
const fs = require("fs");
const data = fs.readFileSync("/Users/john/project/config.json");
// The file path only exists on the developer's machine
# ❌ AI-generated code
import sqlite3
conn = sqlite3.connect('database.db')
# Works locally but fails when database.db doesn't exist in production
How to Debug
Step 1: Check for hardcoded paths
# Search your codebase for absolute paths
grep -r "/Users/" .
grep -r "C:\\\\" .
Step 2: Look for missing file checks
// ✅ Fixed version
const fs = require("fs");
const path = require("path");
const configPath = path.join(__dirname, "config.json");
if (!fs.existsSync(configPath)) {
throw new Error(`Config file not found: ${configPath}`);
}
const data = fs.readFileSync(configPath, "utf8");
Step 3: Use environment variables
// ✅ Production-ready
const dbPath = process.env.DATABASE_PATH || path.join(__dirname, "database.db");
const conn = sqlite3.connect(dbPath);
Prevention Tips
- Always use relative paths or environment variables
- Test in a clean environment (Docker container)
- Document required environment setup
- Use
.env.exampleto show required variables
2. The Async/Await Trap
Symptom
Functions return [object Promise] or undefined instead of actual data. Race conditions cause intermittent failures.
Why AI Does This
AI often forgets await keywords or mixes callback and promise patterns incorrectly.
Common Examples
// ❌ AI-generated code - Missing await
async function getUserData(id) {
const user = fetchUser(id); // Returns Promise, not user data
console.log(user.name); // undefined or error
return user;
}
// ❌ AI-generated code - Mixing patterns
function getData() {
return fetch("/api/data")
.then((res) => res.json())
.then((data) => {
processData(data); // Async operation not awaited
return data;
});
}
How to Debug
Step 1: Look for missing await keywords
// Search for async functions that might be missing await
// Look for: function calls that return promises without await
Step 2: Check promise chains
// ✅ Fixed - Consistent async/await
async function getUserData(id) {
try {
const user = await fetchUser(id);
console.log(user.name);
return user;
} catch (error) {
console.error("Failed to fetch user:", error);
throw error;
}
}
// ✅ Fixed - Proper async handling
async function getData() {
try {
const res = await fetch("/api/data");
const data = await res.json();
await processData(data); // Now properly awaited
return data;
} catch (error) {
console.error("Data fetch failed:", error);
throw error;
}
}
Step 3: Use debugging tools
// Add logging to track promise resolution
console.log("Before fetch");
const data = await fetchData();
console.log("After fetch:", data);
Prevention Tips
- Use ESLint rule:
require-awaitandno-async-promise-executor - Always wrap async operations in try-catch
- Be consistent: use async/await everywhere, not mixed with
.then() - Test timing-dependent code thoroughly
3. The Silent Failure
Symptom
Code doesn’t throw errors, but doesn’t work either. Data disappears into the void.
Why AI Does This
AI generates code without proper error handling, validation, or logging.
Common Examples
// ❌ AI-generated code - Fails silently
app.post("/api/users", (req, res) => {
const user = createUser(req.body);
res.json({ success: true });
});
// If createUser fails, nothing happens
# ❌ AI-generated code - Silent failure
def process_data(data):
try:
result = complex_operation(data)
return result
except:
pass # Swallows all errors
# Errors are hidden, making debugging impossible
How to Debug
Step 1: Add comprehensive logging
// ✅ Fixed with logging
app.post("/api/users", async (req, res) => {
console.log("Creating user with data:", req.body);
try {
const user = await createUser(req.body);
console.log("User created successfully:", user.id);
res.json({ success: true, user });
} catch (error) {
console.error("User creation failed:", error);
res.status(500).json({
success: false,
error: error.message,
});
}
});
Step 2: Validate inputs
// ✅ Add input validation
function createUser(data) {
if (!data.email || !data.name) {
throw new Error("Email and name are required");
}
if (!isValidEmail(data.email)) {
throw new Error("Invalid email format");
}
// Proceed with creation
}
Step 3: Never use empty catch blocks
# ✅ Fixed with proper error handling
def process_data(data):
try:
result = complex_operation(data)
logger.info(f"Processed {len(data)} items successfully")
return result
except ValueError as e:
logger.error(f"Invalid data: {e}")
raise
except Exception as e:
logger.error(f"Processing failed: {e}")
raise ProcessingError("Failed to process data") from e
Prevention Tips
- Never use empty
catch/exceptblocks - Log at key decision points
- Use monitoring tools (Sentry, LogRocket)
- Set up error alerting for production
4. The Memory Leak
Symptom
Application slows down over time, eventually crashes with out-of-memory errors.
Why AI Does This
AI doesn’t clean up resources, close connections, or remove event listeners.
Common Examples
// ❌ AI-generated code - Memory leak
setInterval(() => {
fetchDataAndUpdate();
}, 1000);
// Interval never stops, keeps running forever
// ❌ AI-generated code - Unclosed connections
app.get("/api/data", async (req, res) => {
const db = await createConnection();
const data = await db.query("SELECT * FROM users");
res.json(data);
// Connection never closed!
});
// ❌ AI-generated code - Event listener leak
function setupComponent() {
window.addEventListener("resize", handleResize);
// Listener never removed when component unmounts
}
How to Debug
Step 1: Use memory profiling tools
- Chrome DevTools Memory Profiler
- Node.js
--inspectflag with heap snapshots - Look for growing arrays or objects
Step 2: Check for unclosed resources
// ✅ Fixed - Proper cleanup
let intervalId;
function startPolling() {
intervalId = setInterval(() => {
fetchDataAndUpdate();
}, 1000);
}
function stopPolling() {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
}
// Clean up on unmount
window.addEventListener("beforeunload", stopPolling);
Step 3: Fix connection leaks
// ✅ Fixed - Connection pooling and cleanup
const pool = createConnectionPool();
app.get("/api/data", async (req, res) => {
const connection = await pool.getConnection();
try {
const data = await connection.query("SELECT * FROM users");
res.json(data);
} catch (error) {
res.status(500).json({ error: error.message });
} finally {
connection.release(); // Always release
}
});
Step 4: Remove event listeners
// ✅ Fixed - Proper cleanup in React
useEffect(() => {
const handleResize = () => {
// Handle resize
};
window.addEventListener("resize", handleResize);
// Cleanup function
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
Prevention Tips
- Always clean up intervals, timeouts, and listeners
- Use connection pooling for databases
- Profile memory usage regularly
- Use weak references where appropriate
5. The Race Condition
Symptom
Code works sometimes but fails randomly. Different results on each run.
Why AI Does This
AI doesn’t account for timing issues in concurrent operations.
Common Examples
// ❌ AI-generated code - Race condition
let counter = 0;
async function incrementCounter() {
const current = counter;
await someAsyncOperation();
counter = current + 1; // Multiple calls can overwrite each other
}
// ❌ AI-generated code - State race condition
const [data, setData] = useState(null);
function loadData() {
fetchData().then((result) => setData(result));
// If called multiple times, last response wins
}
How to Debug
Step 1: Add request tracking
// ✅ Fixed - Request cancellation
let currentRequestId = 0;
async function loadData() {
const requestId = ++currentRequestId;
const result = await fetchData();
// Only update if this is still the latest request
if (requestId === currentRequestId) {
setData(result);
}
}
Step 2: Use locks/semaphores
// ✅ Fixed - Simple lock mechanism
let isProcessing = false;
async function processData() {
if (isProcessing) {
console.log("Already processing, skipping");
return;
}
isProcessing = true;
try {
await doWork();
} finally {
isProcessing = false;
}
}
Step 3: Use atomic operations
// ✅ Fixed - Database atomic increment
await db.query("UPDATE counters SET value = value + 1 WHERE id = ?", [
counterId,
]);
// Database handles concurrency correctly
Prevention Tips
- Use AbortController for cancelable requests
- Implement proper locking mechanisms
- Use database transactions for multi-step operations
- Test with concurrent requests
6. The Type Confusion
Symptom
undefined is not a function, Cannot read property of undefined, type errors.
Why AI Does This
AI assumes data structures without validating them.
Common Examples
// ❌ AI-generated code - Assumes array
function processUsers(users) {
return users.map((u) => u.name); // Crashes if users is not an array
}
// Called with: processUsers(null) or processUsers(undefined)
// ❌ AI-generated code - Assumes nested object exists
function getUserCity(user) {
return user.address.city; // Crashes if address is undefined
}
How to Debug
Step 1: Add type checking
// ✅ Fixed with validation
function processUsers(users) {
if (!Array.isArray(users)) {
console.warn("processUsers expected array, got:", typeof users);
return [];
}
return users
.filter((u) => u && u.name) // Filter out invalid entries
.map((u) => u.name);
}
Step 2: Use optional chaining
// ✅ Fixed with optional chaining
function getUserCity(user) {
return user?.address?.city || "Unknown";
}
Step 3: Use TypeScript
// ✅ TypeScript catches these at compile time
interface User {
name: string;
address?: {
city: string;
};
}
function processUsers(users: User[]): string[] {
return users.map((u) => u.name);
}
function getUserCity(user: User): string {
return user.address?.city ?? "Unknown";
}
Prevention Tips
- Use TypeScript or JSDoc for type safety
- Validate API responses before using them
- Use optional chaining (
?.) and nullish coalescing (??) - Add runtime type checking for external data
7. The API Assumption
Symptom
Code breaks when API responses change format or return errors.
Why AI Does This
AI assumes APIs always return expected data in expected format.
Common Examples
// ❌ AI-generated code - Assumes response format
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return data.user; // Assumes data.user always exists
}
# ❌ AI-generated code - No error handling
def fetch_weather(city):
response = requests.get(f'https://api.weather.com/{city}')
return response.json()['temperature']
# Assumes 200 response and temperature field exists
How to Debug
Step 1: Check HTTP status codes
// ✅ Fixed with status checking
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (!data || !data.user) {
throw new Error("Invalid response format");
}
return data.user;
}
Step 2: Validate response structure
# ✅ Fixed with validation
def fetch_weather(city):
try:
response = requests.get(
f'https://api.weather.com/{city}',
timeout=5
)
response.raise_for_status()
data = response.json()
if 'temperature' not in data:
raise ValueError('Temperature data not available')
return data['temperature']
except requests.RequestException as e:
logger.error(f"Weather API error: {e}")
raise WeatherAPIError("Failed to fetch weather data") from e
Step 3: Use schema validation
// ✅ Using Zod for schema validation
import { z } from "zod";
const UserSchema = z.object({
user: z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
}),
});
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
// Throws if data doesn't match schema
const validated = UserSchema.parse(data);
return validated.user;
}
Prevention Tips
- Always check HTTP status codes
- Validate response structure
- Use schema validation libraries (Zod, Joi, Yup)
- Set request timeouts
- Implement retry logic with exponential backoff
8. The Configuration Nightmare
Symptom
App works in development but fails in staging/production with cryptic errors.
Why AI Does This
AI uses default values and doesn’t consider environment differences.
Common Examples
// ❌ AI-generated code
const app = express();
app.listen(3000); // Hardcoded port
mongoose.connect("mongodb://localhost/myapp"); // Local DB only
# ❌ AI-generated code
DEBUG = True # Never turn off debug mode
SECRET_KEY = 'my-secret-key' # Hardcoded secret
How to Debug
Step 1: Create environment config
// ✅ config.js
require("dotenv").config();
module.exports = {
port: process.env.PORT || 3000,
dbUri: process.env.DATABASE_URI || "mongodb://localhost/myapp",
nodeEnv: process.env.NODE_ENV || "development",
jwtSecret: process.env.JWT_SECRET,
};
// Validate required variables
if (!process.env.JWT_SECRET) {
throw new Error("JWT_SECRET environment variable is required");
}
Step 2: Use .env files
# .env.example (commit this)
PORT=3000
DATABASE_URI=mongodb://localhost/myapp
JWT_SECRET=your-secret-here
NODE_ENV=development
# .env (DON'T commit this)
PORT=3000
DATABASE_URI=mongodb://localhost/myapp
JWT_SECRET=actual-secret-key-here
NODE_ENV=development
Step 3: Environment-specific settings
// ✅ Environment-aware configuration
const config = {
development: {
debug: true,
logLevel: "debug",
},
production: {
debug: false,
logLevel: "error",
},
};
const env = process.env.NODE_ENV || "development";
module.exports = config[env];
Prevention Tips
- Use
.envfiles (with.env.example) - Validate required environment variables on startup
- Never commit secrets to git
- Document all required environment variables
- Use different configs per environment
Debugging Toolkit
Essential Tools
Code Quality:
- ESLint - Catch common errors
- Prettier - Consistent formatting
- TypeScript - Type safety
Debugging:
- Chrome DevTools - Browser debugging
- VS Code Debugger - IDE debugging
- console.log() - Quick inspection (remove after)
Monitoring:
- Sentry - Error tracking
- LogRocket - Session replay
- New Relic - Performance monitoring
Testing:
- Jest - Unit testing
- Cypress - E2E testing
- Postman - API testing
Quick Debug Commands
# Node.js debugging
node --inspect-brk app.js
# Then open chrome://inspect
# Memory profiling
node --expose-gc --inspect app.js
# Enable detailed logging
DEBUG=* node app.js
# Check for common issues
npm audit
eslint .
The Debug Checklist
When debugging AI-generated code, ask these questions:
Environment:
- Are all environment variables set?
- Are file paths relative or configurable?
- Does it work in a clean environment?
Async Operations:
- Are all promises properly awaited?
- Is error handling present?
- Are there race conditions?
Data Validation:
- Is input validated before use?
- Are API responses checked?
- Are types verified?
Resource Management:
- Are connections closed properly?
- Are event listeners removed?
- Are intervals/timeouts cleared?
Error Handling:
- Are errors logged?
- Are users notified of failures?
- Do empty catch blocks exist?
Need Expert Help?
Debugging AI-generated code can be time-consuming. If you’re stuck or need production-ready fixes fast, we specialize in identifying and resolving these issues.
Get in Touch to discuss your project.
Related Guides
- AI Code Fixing Guides - Comprehensive fixing strategies
- Privacy Policy - How we handle your data
Last updated: December 14, 2025