...

/

Error Handling and Refinement

Error Handling and Refinement

Add edge cases and exception handling to your budget app.

We started this project by gathering requirements from a simulated client. We built the functional core with AI scaffolding and confirmed it passed basic tests: recording income and logging expenses. But in the real world, users aren’t always perfect. What happens when they make a mistake?

Let’s find out.

Next, we’ll see what happens when users provide invalid input. Run the widget below. When prompted to enter your monthly income, type five hundred instead of 500.

let income = 0.0;

// An array of expense objects, e.g. [{ category: "Food", amount: number }]
let expenses = [];

// Allowed categories
const allowedCategories = [
  "Food", "Rent", "Transport", "Entertainment",
  "Utilities", "Health", "Education", "Other"
];

// --- Core functions ---

function addIncome(amount) {
  // Set or update the monthly income
  income = Number(amount);
  console.log("Income set to $" + income.toFixed(2));
}

function addExpense(category, amount) {
  // Record an expense item
  if (!allowedCategories.includes(category)) {
    console.log("Invalid category. Please choose from: " + allowedCategories.join(", "));
    return;
  }
  expenses.push({ category: category, amount: Number(amount) });
  console.log(`Expense added: ${category} - $${Number(amount).toFixed(2)}`);
}

function deleteExpense(idx) {
  // Delete an expense by zero-based index
  if (idx >= 0 && idx < expenses.length) {
    let removed = expenses.splice(idx, 1)[0];
    console.log(`Deleted expense: ${removed.category} - $${removed.amount.toFixed(2)}`);
    return removed;
  } else {
    console.log("Invalid index. No expense deleted.");
    return null;
  }
}

function totalSpent(expensesList = null) {
  // Return the sum of all expense amounts from a list or the global store
  const list = expensesList || expenses;
  let sum = 0;
  for (let entry of list) {
    sum += entry.amount;
  }
  return sum;
}

function computeCategoryTotals(expensesList = null) {
  // Return an object mapping category -> total amount from expenses
  const list = expensesList || expenses;
  const totals = {};
  for (let entry of list) {
    if (!totals[entry.category]) {
      totals[entry.category] = 0;
    }
    totals[entry.category] += entry.amount;
  }
  return totals;
}

function printReport(incomeValue = null, expensesList = null) {
  // Print a formatted budget report with category totals and status line
  const inc = incomeValue !== null ? Number(incomeValue) : income;
  const list = expensesList || expenses;
  const total = totalSpent(list);
  const remaining = inc - total;
  const categoryTotals = computeCategoryTotals(list);

  console.log("\n--- Budget Report ---");
  console.log(`Income: $${inc.toFixed(2)}`);
  console.log(`Total Spent: $${total.toFixed(2)}`);

  console.log("\nCategory Spending:");
  for (let category in categoryTotals) {
    console.log(`  - ${category}: $${categoryTotals[category].toFixed(2)}`);
  }

  console.log("---------------------------");
  console.log(`Remaining Balance: $${remaining.toFixed(2)}`);
}

function showMenu() {
  // Display simple command menu for the CLI
  console.log("\n--- Budget Tracker Menu ---");
  console.log("1. Set Monthly Income");
  console.log("2. Add an Expense");
  console.log("3. Delete an Expense");
  console.log("4. Show Budget Report");
  console.log("5. Reset All Data");
  console.log("6. Exit");
}

function listExpenses() {
  // Display all expenses with their index
  if (expenses.length === 0) {
    console.log("No expenses recorded yet.");
    return;
  }
  console.log("\nCurrent Expenses:");
  expenses.forEach((expense, i) => {
    console.log(`${i}: ${expense.category} - $${expense.amount.toFixed(2)}`);
  });
}

function resetData() {
  // Clear income and expenses; return to a clean initial state
  income = 0.0;
  expenses = [];
  console.log("All budget data has been cleared.");
}

// --- CLI main loop ---
function runCLI() {
  // Command-line style interface
  while (true) {
    showMenu();
    let choice = prompt("Choose an option (1-6): ");

    if (choice === "1") {
      let amount = prompt("Enter your monthly income: $");
      addIncome(amount);

    } else if (choice === "2") {
      console.log("\nAvailable Categories: " + allowedCategories.join(", "));
      let category = prompt("Enter the expense category: ");
      let amount = prompt("Enter the expense amount: $");
      addExpense(category, amount);

    } else if (choice === "3") {
      listExpenses();
      if (expenses.length > 0) {
        let idx = Number(prompt("Enter the index of the expense to delete: "));
        deleteExpense(idx);
      }

    } else if (choice === "4") {
      printReport();

    } else if (choice === "5") {
      resetData();

    } else if (choice === "6") {
      console.log("Exiting Budget Tracker.");
      break;

    } else {
      console.log("Invalid choice. Please select a valid option.");
    }
  }
}

// Kick off the app
runCLI();
Test your project with the wrong user input

The income is set to NaN: not a valid number.

What went wrong?

The AI-generated code expected a numeric value. It attempted to convert the text "five hundred" into a number using Number(). Because the text was not numeric, the result was NaN (“Not a Number”) and the app didn't work as expected.

The generated code was built for the simplest case: users always enter valid numbers. That works for a prototype, but it’s brittle. It’s missing the error handling needed for a robust app.

This is a classic edge case—uncommon, but very possible. In our test, the issue came from a user typing text instead of a number. To fix this, we’ll also handle exceptions, the ...