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.
Prerequisites
- 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
Add numeric validation rules to your project
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.
1---2description: Numeric input validation rules3globs: "src/**/*.ts,src/**/*.tsx"4alwaysApply: true5---67## Numeric Validation Rules8- ALWAYS validate numeric inputs at function entry point9- Prices and monetary values: must be >= 0, max 2 decimal places10- Quantities: must be positive integers (> 0)11- Percentages: must be between 0 and 100 inclusive12- Array indices: must be >= 0 and < array.length13- Division: ALWAYS check divisor !== 0 before dividing14- Age/count fields: must be non-negative integers15- Throw descriptive errors: 'Expected positive integer for quantity, got: ${value}'16- Use Number.isFinite() to reject NaN and InfinityExpected result: Cursor automatically includes boundary checks when generating functions with numeric parameters.
Generate a validation utility module
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.
1// Cursor Chat prompt (Cmd+L):2// Create a numeric validation utility module at3// src/utils/validate.ts with these functions:4// - assertPositive(value, name) — throws if not > 05// - assertNonNegative(value, name) — throws if < 06// - assertRange(value, min, max, name)7// - assertInteger(value, name)8// - assertFinite(value, name)9// Each should throw a descriptive error with the field name.1011export function assertPositive(value: number, name: string): void {12 if (!Number.isFinite(value) || value <= 0) {13 throw new RangeError(14 `Expected positive number for ${name}, got: ${value}`15 );16 }17}1819export function assertNonNegative(value: number, name: string): void {20 if (!Number.isFinite(value) || value < 0) {21 throw new RangeError(22 `Expected non-negative number for ${name}, got: ${value}`23 );24 }25}2627export function assertRange(28 value: number, min: number, max: number, name: string29): void {30 if (!Number.isFinite(value) || value < min || value > max) {31 throw new RangeError(32 `Expected ${name} between ${min} and ${max}, got: ${value}`33 );34 }35}Expected result: A validation utility module with typed guard functions Cursor can import in generated code.
Prompt Cursor to generate validated functions
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.
1// Cursor Chat prompt (Cmd+L):2// @src/utils/validate.ts Create a calculateDiscount3// function that takes price (non-negative), quantity4// (positive integer), and discountPercent (0-100).5// Use our validation utilities for boundary checks.6// Return the final price after discount.78// Expected output:9import { assertNonNegative, assertPositive, assertRange } from '@/utils/validate';1011export function calculateDiscount(12 price: number,13 quantity: number,14 discountPercent: number15): number {16 assertNonNegative(price, 'price');17 assertPositive(quantity, 'quantity');18 assertRange(discountPercent, 0, 100, 'discountPercent');1920 const subtotal = price * quantity;21 const discount = subtotal * (discountPercent / 100);22 return Math.round((subtotal - discount) * 100) / 100;23}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.
Audit existing functions for missing validation
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.
1// Cursor Chat prompt (Cmd+L, Ask mode):2// @codebase Find all functions in src/ that accept3// numeric parameters (price, amount, quantity, count,4// percentage, index, age, etc.) but do NOT have any5// validation or boundary checks at the start of the6// function body. List each file, function name, and7// which parameter needs validation.Expected result: A list of unvalidated numeric parameters across your codebase, prioritized for adding guards.
Add validation to existing functions with Cmd+K
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.
1// Select the function body, press Cmd+K:2// @src/utils/validate.ts Add boundary validation to all3// numeric parameters using our validation utilities.4// price must be non-negative, quantity must be a positive5// integer. Add the checks at the top of the function6// before any existing logic.Expected result: Guard clauses added at the top of the function using your standard validation utilities.
Complete working example
1/**2 * Numeric validation utilities for boundary checking.3 * Import these in any function that accepts numeric inputs.4 */56export function assertFinite(value: number, name: string): void {7 if (!Number.isFinite(value)) {8 throw new TypeError(9 `Expected finite number for ${name}, got: ${value}`10 );11 }12}1314export function assertPositive(value: number, name: string): void {15 assertFinite(value, name);16 if (value <= 0) {17 throw new RangeError(18 `Expected positive number for ${name}, got: ${value}`19 );20 }21}2223export function assertNonNegative(value: number, name: string): void {24 assertFinite(value, name);25 if (value < 0) {26 throw new RangeError(27 `Expected non-negative number for ${name}, got: ${value}`28 );29 }30}3132export function assertInteger(value: number, name: string): void {33 assertFinite(value, name);34 if (!Number.isInteger(value)) {35 throw new TypeError(36 `Expected integer for ${name}, got: ${value}`37 );38 }39}4041export function assertRange(42 value: number,43 min: number,44 max: number,45 name: string46): void {47 assertFinite(value, name);48 if (value < min || value > max) {49 throw new RangeError(50 `Expected ${name} between ${min} and ${max}, got: ${value}`51 );52 }53}5455export function assertPositiveInteger(value: number, name: string): void {56 assertInteger(value, name);57 assertPositive(value, name);58}5960export function assertSafeDivisor(value: number, name: string): void {61 assertFinite(value, name);62 if (value === 0) {63 throw new RangeError(64 `Division by zero: ${name} cannot be 0`65 );66 }67}Common mistakes when ensuring input validation in Cursor-generated code
Why it's a problem: Only checking for undefined/null but not NaN or Infinity
How to avoid: Add 'Use Number.isFinite() to reject NaN and Infinity' to your .cursorrules validation rules.
Why it's a problem: Placing validation after logic instead of before
How to avoid: In your prompt, specify 'Add boundary checks at the TOP of the function, before any existing logic.'
Why it's a problem: Using generic error messages that do not include the invalid value
How to avoid: 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
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Create a TypeScript validation utility module with functions: assertPositive, assertNonNegative, assertRange, assertInteger, assertFinite, and assertSafeDivisor. Each function takes a value and a field name, validates the constraint, and throws a descriptive RangeError or TypeError with the field name and actual value on failure.
In Cursor (Cmd+K, with function selected): @src/utils/validate.ts Add boundary validation to all numeric parameters. Use our validation utilities (assertPositive, assertNonNegative, assertRange). Place checks at the top of the function. Include descriptive error messages.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation