VibeCodeXray
Back to Blog
Article

Common Patterns in Broken AI Code

VCX TeamMarch 9, 20268 min read

Common Patterns in Broken AI Code

After analyzing thousands of lines of AI-generated code, patterns emerge. The same mistakes repeat across projects, languages, and use cases.

Recognizing these patterns is the first step to catching them. Here are the most common ways AI code breaks—and how to fix it.

Pattern 1: The Trusting Input Pattern

AI code often assumes inputs are well-formed and safe. They're not.

The Problem

// AI generated - assumes userId is always valid
async function getUser(userId) {
  return db.query(`SELECT * FROM users WHERE id = ${userId}`);
}

This code works perfectly when userId is "123". But what if it's "123 OR 1=1"?

Why AI Does This

AI models are trained on example code, not production code. Examples often skip validation for clarity. AI learns that pattern and reproduces it.

The Fix

Always validate and sanitize inputs:

async function getUser(userId) {
  if (!/^\d+$/.test(userId)) {
    throw new Error('Invalid user ID');
  }
  return db.query('SELECT * FROM users WHERE id = ?', [userId]);
}

Red Flags to Watch For

  • String concatenation in queries
  • Direct use of request parameters
  • Missing type checks
  • No validation functions

Pattern 2: The Incomplete Error Pattern

AI generates try-catch blocks, but the catches are often useless.

The Problem

// AI generated - catches and swallows
async function processPayment(paymentData) {
  try {
    const result = await paymentGateway.charge(paymentData);
    return result;
  } catch (error) {
    console.log(error);
  }
}

This catches errors but doesn't handle them. The caller gets undefined instead of knowing the payment failed.

Why AI Does This

AI sees many examples with console.log(error) and learns this as "how to handle errors." It doesn't understand that production code needs proper error handling.

The Fix

Handle errors meaningfully:

async function processPayment(paymentData) {
  try {
    const result = await paymentGateway.charge(paymentData);
    return { success: true, result };
  } catch (error) {
    logger.error('Payment failed', { error, paymentData: paymentData.id });
    return { success: false, error: error.message };
  }
}

Red Flags to Watch For

  • Empty catch blocks
  • console.log in catch blocks
  • No error propagation
  • No user feedback

Pattern 3: The Implicit State Pattern

AI code often modifies state without being explicit about it.

The Problem

// AI generated - mutates global state
let currentUser = null;

function setUserData(data) {
  currentUser = data;
  processQueue();
}

This function mutates currentUser as a side effect. Other code might not expect this.

Why AI Does This

AI focuses on the immediate task—"process user data"—without considering the broader implications of global state mutations.

The Fix

Make state changes explicit:

function setUserData(data) {
  // Return new state instead of mutating
  return {
    user: data,
    shouldProcessQueue: true
  };
}

// Or use a state manager with explicit updates
stateManager.update('user', data);

Red Flags to Watch For

  • Global variables being modified
  • No return value but changes occur
  • Side effects in "getter" functions
  • Missing state documentation

Pattern 4: The Happy Path Only Pattern

AI generates code that works when everything goes right. But what about when it doesn't?

The Problem

// AI generated - no edge cases
function calculateDiscount(price, tier) {
  if (tier === 'premium') return price * 0.8;
  if (tier === 'basic') return price * 0.9;
  return price;
}

What if tier is null? Or 'PREMIUM'? Or an object?

Why AI Does This

AI models optimize for the common case in their training data. Edge cases are rare in examples, so AI doesn't learn to handle them.

The Fix

Handle all cases explicitly:

function calculateDiscount(price, tier) {
  if (typeof price !== 'number' || price < 0) {
    throw new Error('Invalid price');
  }
  
  const normalizedTier = (tier || '').toLowerCase();
  
  const discounts = {
    premium: 0.8,
    basic: 0.9,
    free: 1.0
  };
  
  const discount = discounts[normalizedTier] ?? 1.0;
  return price * discount;
}

Red Flags to Watch For

  • No null checks
  • No type validation
  • Missing else branches
  • No default cases in switches

Pattern 5: The Overspecified Pattern

Sometimes AI code is too specific to work in real conditions.

The Problem

// AI generated - too specific
function parseApiResponse(response) {
  return response.data.users[0].profile.name;
}

If the API returns an empty users array, this crashes with Cannot read property 'profile' of undefined.

Why AI Does This

AI training data often shows ideal responses. The model learns that APIs always return the expected structure.

The Fix

Handle variability:

function parseApiResponse(response) {
  return response?.data?.users?.[0]?.profile?.name ?? 'Unknown';
}

Red Flags to Watch For

  • Deep property access without checks
  • Assuming array indices exist
  • No null coalescing
  • No fallback values

Pattern 6: The Security Theater Pattern

AI sometimes generates security code that looks correct but isn't.

The Problem

// AI generated - looks secure, isn't
function hashPassword(password) {
  return btoa(password); // Base64 encoding, not hashing!
}

// Or even worse
function checkAuth(token) {
  return token.startsWith('admin_');
}

Base64 is encoding, not hashing. Token prefix checking is trivial to bypass.

Why AI Does This

AI doesn't understand cryptography. It sees patterns like "transform password" and applies whatever transformation it knows.

The Fix

Use proper security libraries:

import bcrypt from 'bcrypt';

async function hashPassword(password) {
  return bcrypt.hash(password, 10);
}

async function verifyPassword(password, hash) {
  return bcrypt.compare(password, hash);
}

Red Flags to Watch For

  • Custom encryption/hashing
  • Weak algorithms (MD5, SHA1)
  • Security checks that can be spoofed
  • Hardcoded keys or salts

Pattern 7: The Async/Await Without Error Handling Pattern

Modern AI uses async/await, but often forgets error handling.

The Problem

// AI generated - unhandled promise rejection
async function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  return data;
}

If the fetch fails, this throws an unhandled promise rejection.

Why AI Does This

Many examples show async/await without try-catch for brevity. AI learns this pattern and doesn't distinguish between example code and production code.

The Fix

Always handle async errors:

async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    logger.error('Failed to fetch user', { userId, error });
    throw new UserFetchError(userId, error);
  }
}

Red Flags to Watch For

  • Async functions without try-catch
  • Awaiting without checking response status
  • No error context
  • Silent failures

Pattern 8: The Premature Optimization Pattern

AI sometimes optimizes for the wrong things.

The Problem

// AI generated - caching without invalidation
const cache = {};

function getUser(userId) {
  if (cache[userId]) {
    return cache[userId];
  }
  const user = db.query(`SELECT * FROM users WHERE id = ${userId}`);
  cache[userId] = user;
  return user;
}

This cache never invalidates. If the user updates their profile, they'll see stale data forever.

Why AI Does This

AI sees caching examples and applies the pattern without understanding cache invalidation—one of the hardest problems in CS.

The Fix

Include cache invalidation or use a proper cache:

const cache = new Map();

function getUser(userId) {
  const cached = cache.get(userId);
  if (cached && Date.now() - cached.timestamp < 60000) {
    return cached.data;
  }
  
  const user = db.query('SELECT * FROM users WHERE id = ?', [userId]);
  cache.set(userId, { data: user, timestamp: Date.now() });
  return user;
}

Red Flags to Watch For

  • Caching without TTL
  • No cache invalidation logic
  • Memory leaks in caches
  • Stale data issues

How to Spot These Patterns

Code Review Checklist

When reviewing AI-generated code, look for:

  1. Input handling - Is all input validated?
  2. Error handling - Are errors properly caught and handled?
  3. State management - Are state changes explicit?
  4. Edge cases - What happens with unexpected input?
  5. Security - Is security real or theater?
  6. Async - Are promises handled?
  7. Caching - Is there invalidation?

Automated Detection

Use static analysis tools to catch patterns:

# ESLint rules for common issues
npx eslint --rule 'no-implicit-globals: error' \
           --rule 'no-unsafe-optional-chaining: error' \
           --rule 'require-atomic-updates: error'

The Fix-First Mindset

When you spot these patterns, don't just fix the immediate issue. Ask:

  1. Why did AI generate this? - Understanding helps you catch similar issues
  2. What else might be affected? - Patterns tend to repeat
  3. How can I prevent this? - Add lints, tests, or prompts

Moving Forward

AI coding assistants are powerful tools. They're not perfect, but recognizing their failure patterns makes you a better developer.

The goal isn't to stop using AI—it's to use it wisely. Know what to look for, fix what's broken, and ship better code.


Want automated detection of these patterns? VCX scans AI-generated code for common failure patterns.

Share this article

Get Started

Ready to secure your AI code?

Get started with VCX and audit your AI-generated code before it breaks production.