Skip to main content
RapidDev - Software Development Agency
cursor-tutorial

How to ensure input validation in Cursor-generated code

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.

What you'll learn

  • How to add numeric validation rules to .cursorrules
  • How to prompt Cursor to include boundary checks automatically
  • How to create a reusable validation utility with Cursor
  • How to audit existing code for missing numeric guards
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read10-15 minCursor Free+, TypeScript/JavaScriptMarch 2026RapidDev Engineering Team
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.

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

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.

.cursor/rules/validation.mdc
1---
2description: Numeric input validation rules
3globs: "src/**/*.ts,src/**/*.tsx"
4alwaysApply: true
5---
6
7## Numeric Validation Rules
8- ALWAYS validate numeric inputs at function entry point
9- Prices and monetary values: must be >= 0, max 2 decimal places
10- Quantities: must be positive integers (> 0)
11- Percentages: must be between 0 and 100 inclusive
12- Array indices: must be >= 0 and < array.length
13- Division: ALWAYS check divisor !== 0 before dividing
14- Age/count fields: must be non-negative integers
15- Throw descriptive errors: 'Expected positive integer for quantity, got: ${value}'
16- 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.

src/utils/validate.ts
1// Cursor Chat prompt (Cmd+L):
2// Create a numeric validation utility module at
3// src/utils/validate.ts with these functions:
4// - assertPositive(value, name) — throws if not > 0
5// - assertNonNegative(value, name) — throws if < 0
6// - assertRange(value, min, max, name)
7// - assertInteger(value, name)
8// - assertFinite(value, name)
9// Each should throw a descriptive error with the field name.
10
11export 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}
18
19export 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}
26
27export function assertRange(
28 value: number, min: number, max: number, name: string
29): 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.

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.

src/services/pricing.ts
1// Cursor Chat prompt (Cmd+L):
2// @src/utils/validate.ts Create a calculateDiscount
3// function that takes price (non-negative), quantity
4// (positive integer), and discountPercent (0-100).
5// Use our validation utilities for boundary checks.
6// Return the final price after discount.
7
8// Expected output:
9import { assertNonNegative, assertPositive, assertRange } from '@/utils/validate';
10
11export function calculateDiscount(
12 price: number,
13 quantity: number,
14 discountPercent: number
15): number {
16 assertNonNegative(price, 'price');
17 assertPositive(quantity, 'quantity');
18 assertRange(discountPercent, 0, 100, 'discountPercent');
19
20 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.

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
1// Cursor Chat prompt (Cmd+L, Ask mode):
2// @codebase Find all functions in src/ that accept
3// numeric parameters (price, amount, quantity, count,
4// percentage, index, age, etc.) but do NOT have any
5// validation or boundary checks at the start of the
6// function body. List each file, function name, and
7// 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.

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

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

Complete working example

src/utils/validate.ts
1/**
2 * Numeric validation utilities for boundary checking.
3 * Import these in any function that accepts numeric inputs.
4 */
5
6export 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}
13
14export 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}
22
23export 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}
31
32export 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}
40
41export function assertRange(
42 value: number,
43 min: number,
44 max: number,
45 name: string
46): 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}
54
55export function assertPositiveInteger(value: number, name: string): void {
56 assertInteger(value, name);
57 assertPositive(value, name);
58}
59
60export 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.

ChatGPT Prompt

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.

Cursor Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.