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();
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 ...