🟡 MediumWhat's Wrong?🐛 Debug Challenge

RegExp stateful lastIndex trap with global flag

Buggy Code — Can you spot the issue?

const emailRegex = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/gi;

function isValidEmail(email) {
  return emailRegex.test(email);
}

console.log(isValidEmail('user@example.com')); // true
console.log(isValidEmail('user@example.com')); // false?!
console.log(isValidEmail('user@example.com')); // true again

Fixed Code

// Fix 1: Remove global flag (not needed for simple test)
const emailRegex = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i;

function isValidEmail(email) {
  return emailRegex.test(email);
}

// Fix 2: Reset lastIndex before each call
const emailRegexG = /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/gi;
function isValidEmailSafe(email) {
  emailRegexG.lastIndex = 0; // reset before use
  return emailRegexG.test(email);
}

// Fix 3: Create a new regex each call (safest, slight perf cost)
function isValidEmailNew(email) {
  return /^[\w.-]+@[\w.-]+\.[a-z]{2,}$/i.test(email);
}

console.log(isValidEmail('user@example.com')); // true
console.log(isValidEmail('user@example.com')); // true ✓

Bug Explained

Bug: With the g (global) flag, RegExp.test() advances lastIndex after each match. Next call starts from that position — not from 0 — causing it to "fail" and reset lastIndex to 0.

Explanation: The global (g) flag makes regex stateful via lastIndex. After a successful test(), lastIndex = end of match. Next test() starts there — fails at end of string — resets lastIndex to 0. Alternating true/false.

Key Insight: Never use the g flag on a shared regex unless using exec() in a loop. For test() and match() with simple checks, omit g or reset lastIndex = 0 before each call.

More What's Wrong? Debug Challenges

🟢 EasyAccidental global variable🟡 MediumWrong comparison with NaN🔴 HardInfinite re-render in React useEffect🟡 MediumArray sort mutates original

Practice spotting bugs live →

38 debug challenges with AI hints

🐛 Try Debug Lab