# How to ensure input validation in Cursor-generated code

- Tool: Cursor
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Cursor Free+, TypeScript/JavaScript
- Last updated: March 2026

## TL;DR

Cursor frequently generates functions that accept numeric inputs without range checks, leading to runtime errors, negative values in price fields, or division by zero. By adding validation rules to .cursorrules and including explicit boundary requirements in your prompts, you ensure Cursor always generates guard clauses for numeric inputs in critical code paths.

## Ensuring input validation in Cursor-generated code

Cursor prioritizes generating working logic over defensive programming. Functions that handle prices, quantities, percentages, or array indices often lack boundary checks. This tutorial shows you how to make Cursor generate robust input validation for every numeric parameter in critical functions.

## Before you start

- Cursor installed with a project open
- Functions that accept numeric inputs (prices, quantities, ages, etc.)
- TypeScript or JavaScript project
- Familiarity with Cmd+K and Cmd+L shortcuts

## Step-by-step guide

### 1. Add numeric validation rules to your project

Create a .cursor/rules file that mandates boundary checks for common numeric patterns. This rule applies globally to all generated code, ensuring Cursor never skips validation on numeric inputs.

```
---
description: Numeric input validation rules
globs: "src/**/*.ts,src/**/*.tsx"
alwaysApply: true
---

## Numeric Validation Rules
- ALWAYS validate numeric inputs at function entry point
- Prices and monetary values: must be >= 0, max 2 decimal places
- Quantities: must be positive integers (> 0)
- Percentages: must be between 0 and 100 inclusive
- Array indices: must be >= 0 and < array.length
- Division: ALWAYS check divisor !== 0 before dividing
- Age/count fields: must be non-negative integers
- Throw descriptive errors: 'Expected positive integer for quantity, got: ${value}'
- Use Number.isFinite() to reject NaN and Infinity
```

**Expected result:** Cursor automatically includes boundary checks when generating functions with numeric parameters.

### 2. Generate a validation utility module

Use Cursor Chat (Cmd+L) to create a reusable validation module. This gives you standard validation functions that Cursor can import in generated code, ensuring consistent error messages and check patterns.

```
// Cursor Chat prompt (Cmd+L):
// Create a numeric validation utility module at
// src/utils/validate.ts with these functions:
// - assertPositive(value, name) — throws if not > 0
// - assertNonNegative(value, name) — throws if < 0
// - assertRange(value, min, max, name)
// - assertInteger(value, name)
// - assertFinite(value, name)
// Each should throw a descriptive error with the field name.

export function assertPositive(value: number, name: string): void {
  if (!Number.isFinite(value) || value <= 0) {
    throw new RangeError(
      `Expected positive number for ${name}, got: ${value}`
    );
  }
}

export function assertNonNegative(value: number, name: string): void {
  if (!Number.isFinite(value) || value < 0) {
    throw new RangeError(
      `Expected non-negative number for ${name}, got: ${value}`
    );
  }
}

export function assertRange(
  value: number, min: number, max: number, name: string
): void {
  if (!Number.isFinite(value) || value < min || value > max) {
    throw new RangeError(
      `Expected ${name} between ${min} and ${max}, got: ${value}`
    );
  }
}
```

**Expected result:** A validation utility module with typed guard functions Cursor can import in generated code.

### 3. Prompt Cursor to generate validated functions

When asking Cursor to generate functions with numeric inputs, reference the validation module and explicitly mention boundary requirements. This produces functions with guard clauses built in.

```
// Cursor Chat prompt (Cmd+L):
// @src/utils/validate.ts Create a calculateDiscount
// function that takes price (non-negative), quantity
// (positive integer), and discountPercent (0-100).
// Use our validation utilities for boundary checks.
// Return the final price after discount.

// Expected output:
import { assertNonNegative, assertPositive, assertRange } from '@/utils/validate';

export function calculateDiscount(
  price: number,
  quantity: number,
  discountPercent: number
): number {
  assertNonNegative(price, 'price');
  assertPositive(quantity, 'quantity');
  assertRange(discountPercent, 0, 100, 'discountPercent');

  const subtotal = price * quantity;
  const discount = subtotal * (discountPercent / 100);
  return Math.round((subtotal - discount) * 100) / 100;
}
```

> Pro tip: Include the constraint in parentheses right after the parameter name in your prompt: 'price (non-negative), quantity (positive integer)'. Cursor picks this up reliably.

**Expected result:** A function with all numeric parameters validated before any logic executes.

### 4. Audit existing functions for missing validation

Use Cursor's Ask mode to scan your codebase for functions that accept numeric parameters without validation. This helps you identify gaps in existing code that need guard clauses added.

```
// Cursor Chat prompt (Cmd+L, Ask mode):
// @codebase Find all functions in src/ that accept
// numeric parameters (price, amount, quantity, count,
// percentage, index, age, etc.) but do NOT have any
// validation or boundary checks at the start of the
// function body. List each file, function name, and
// which parameter needs validation.
```

**Expected result:** A list of unvalidated numeric parameters across your codebase, prioritized for adding guards.

### 5. Add validation to existing functions with Cmd+K

For each unvalidated function found in the audit, select the function, press Cmd+K, and ask Cursor to add boundary checks. Reference the validation utility so Cursor uses your standard functions instead of inline checks.

```
// Select the function body, press Cmd+K:
// @src/utils/validate.ts Add boundary validation to all
// numeric parameters using our validation utilities.
// price must be non-negative, quantity must be a positive
// integer. Add the checks at the top of the function
// before any existing logic.
```

**Expected result:** Guard clauses added at the top of the function using your standard validation utilities.

## Complete code example

File: `src/utils/validate.ts`

```typescript
/**
 * Numeric validation utilities for boundary checking.
 * Import these in any function that accepts numeric inputs.
 */

export function assertFinite(value: number, name: string): void {
  if (!Number.isFinite(value)) {
    throw new TypeError(
      `Expected finite number for ${name}, got: ${value}`
    );
  }
}

export function assertPositive(value: number, name: string): void {
  assertFinite(value, name);
  if (value <= 0) {
    throw new RangeError(
      `Expected positive number for ${name}, got: ${value}`
    );
  }
}

export function assertNonNegative(value: number, name: string): void {
  assertFinite(value, name);
  if (value < 0) {
    throw new RangeError(
      `Expected non-negative number for ${name}, got: ${value}`
    );
  }
}

export function assertInteger(value: number, name: string): void {
  assertFinite(value, name);
  if (!Number.isInteger(value)) {
    throw new TypeError(
      `Expected integer for ${name}, got: ${value}`
    );
  }
}

export function assertRange(
  value: number,
  min: number,
  max: number,
  name: string
): void {
  assertFinite(value, name);
  if (value < min || value > max) {
    throw new RangeError(
      `Expected ${name} between ${min} and ${max}, got: ${value}`
    );
  }
}

export function assertPositiveInteger(value: number, name: string): void {
  assertInteger(value, name);
  assertPositive(value, name);
}

export function assertSafeDivisor(value: number, name: string): void {
  assertFinite(value, name);
  if (value === 0) {
    throw new RangeError(
      `Division by zero: ${name} cannot be 0`
    );
  }
}
```

## Common mistakes

- **Only checking for undefined/null but not NaN or Infinity** — TypeScript's number type includes NaN and Infinity. Cursor often adds null checks but forgets Number.isFinite(), allowing bad values through. Fix: Add 'Use Number.isFinite() to reject NaN and Infinity' to your .cursorrules validation rules.
- **Placing validation after logic instead of before** — Cursor sometimes adds validation as an afterthought at the end of the function, after the invalid value has already been used. Fix: In your prompt, specify 'Add boundary checks at the TOP of the function, before any existing logic.'
- **Using generic error messages that do not include the invalid value** — Cursor may throw 'Invalid input' without specifying which parameter failed or what the actual value was, making debugging difficult. Fix: Require descriptive errors in .cursorrules: 'Throw errors with field name and actual value.'

## Best practices

- Create a shared validation utility module and reference it with @file in Cursor prompts
- Add boundary requirements directly in your prompt using parenthetical notation: 'price (>= 0)'
- Include Number.isFinite() checks in your .cursorrules to catch NaN and Infinity
- Place all validation at the top of functions before any business logic
- Use descriptive error messages that include the parameter name and actual value
- Audit your codebase periodically with @codebase search for unvalidated numeric inputs
- Use TypeScript branded types (e.g., PositiveNumber) for compile-time enforcement on critical values

## Frequently asked questions

### Why does Cursor skip numeric validation so often?

AI models optimize for the happy path to produce concise code. Defensive checks are seen as boilerplate unless explicitly requested. Adding validation rules to .cursorrules makes Cursor include them by default.

### Should I validate at the function level or the API boundary?

Both. Validate at the API boundary for user-facing inputs and at the function level for business-critical operations like financial calculations. Use your .cursorrules to specify which functions need validation.

### Can I use Zod instead of manual validation?

Yes. Zod is excellent for schema validation. Add to your .cursorrules: 'Use Zod schemas for input validation. Import from @/schemas/{domain}.ts.' Cursor generates Zod schemas well with proper context.

### Will adding validation to every function hurt performance?

No. Numeric validation (comparison and type checks) costs nanoseconds per call. The safety benefit far outweighs the negligible performance cost. Only skip validation in tight loops processing millions of pre-validated items.

### How do I handle validation errors in Cursor-generated API routes?

Return HTTP 400 with structured error objects. Add to your .cursorrules: 'API validation errors return 400 with { error: string, field: string, value: any }.' Cursor will generate consistent error responses.

---

Source: https://www.rapidevelopers.com/cursor-tutorial/how-to-ensure-cursor-ai-includes-boundary-checks-for-numeric-inputs-in-critical-functions
© RapidDev — https://www.rapidevelopers.com/cursor-tutorial/how-to-ensure-cursor-ai-includes-boundary-checks-for-numeric-inputs-in-critical-functions
