Salesforce Validation Rules: 20 Real-World Examples Every Admin Should Know
Anatomy of a rule, ISCHANGED/ISNEW/PRIORVALUE/REGEX patterns, 20 working examples, error message best practices, and the gotchas that get missed.

TL;DR
- A Validation Rule is a formula that, when true, blocks save and shows an error.
- Use the four core functions:
ISCHANGED,ISNEW,PRIORVALUE, andREGEX. Together they handle ~90% of cases.- 20 working examples below, by category — text format, dates, money, status transitions, cross-object, record-type-specific.
- Beware: validation rules are skipped by mass transfer and some integration paths. They're the cheapest data-quality enforcement on the platform — but not the only one.
Validation rules are the most underrated tool in the admin toolbox. They cost nothing to build, run instantly, and prevent more bad data than any downstream cleanup script. This guide is the canonical 2026 reference: anatomy, patterns, and 20 working examples you can paste into your org tonight.
Anatomy of a validation rule
A validation rule is two things:
- An error condition formula that returns
TRUEorFALSE. WhenTRUE, the save blocks. - An error message that the user sees.
Rule Name: Email_Required_For_Customer
Error Condition: AND(
ISPICKVAL(Type, 'Customer'),
ISBLANK(Email)
)
Error Message: Email is required for Customers.
Error Location: Field "Email"
The error formula is true → save blocks → user sees the message. The "error location" controls where the message appears (next to a specific field, or at the top of the page).
The four core functions
Internalize these and you'll write 90% of validation rules without looking anything up.
| Function | Use when |
|---|---|
ISCHANGED(field) | The field changed in this save (insert: always TRUE; update: depends) |
ISNEW() | The record is being inserted (not updated) |
PRIORVALUE(field) | The value of field before this save (only useful with ISCHANGED) |
REGEX(text, pattern) | Text matches a regex pattern. Best for format validation |
Combined examples:
// Block changes to Stage from "Closed Won" back to anything else
AND(
ISCHANGED(StageName),
PRIORVALUE(StageName) = 'Closed Won'
)
// Require Email format on creation only (not edits)
AND(
ISNEW(),
NOT(REGEX(Email, '^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'))
)
20 real-world examples
Each is a formula you can paste directly. Adjust field API names to match your org.
Text format (Examples 1–4)
1. Email format validation
AND(
NOT(ISBLANK(Email)),
NOT(REGEX(Email, '^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'))
)
Error: "Email must be in valid format (user@example.com)."
2. US phone number format
AND(
NOT(ISBLANK(Phone)),
NOT(REGEX(Phone, '^\\(?\\d{3}\\)?[\\s.-]?\\d{3}[\\s.-]?\\d{4}$'))
)
Error: "Phone must be in (555) 123-4567 format."
3. ZIP code validation (US)
AND(
NOT(ISBLANK(MailingPostalCode)),
NOT(REGEX(MailingPostalCode, '^\\d{5}(-\\d{4})?$'))
)
Error: "ZIP must be 5 digits or 5+4 (12345 or 12345-6789)."
4. No special characters in account name
NOT(REGEX(Name, '^[A-Za-z0-9\\s.,&\\-\']+$'))
Error: "Account Name can only contain letters, numbers, spaces, and standard punctuation."
Dates (Examples 5–8)
5. Close Date can't be in the past on create
AND(
ISNEW(),
CloseDate < TODAY()
)
Error: "Close Date cannot be in the past."
6. Close Date must be at least 7 days from today on create
AND(
ISNEW(),
CloseDate < TODAY() + 7
)
Error: "Close Date must be at least 7 days out."
7. Birth date must be in the past
AND(
NOT(ISBLANK(Birthdate)),
Birthdate > TODAY()
)
Error: "Birth date cannot be in the future."
8. End Date must be after Start Date
AND(
NOT(ISBLANK(End_Date__c)),
NOT(ISBLANK(Start_Date__c)),
End_Date__c < Start_Date__c
)
Error: "End Date must be on or after Start Date."
Money / amounts (Examples 9–11)
9. Discount can't exceed 50%
AND(
NOT(ISBLANK(Discount__c)),
Discount__c > 0.5
)
Error: "Discount cannot exceed 50%."
10. Opportunity Amount required for Closed Won
AND(
ISPICKVAL(StageName, 'Closed Won'),
OR(ISBLANK(Amount), Amount <= 0)
)
Error: "Amount is required and must be positive when Stage is Closed Won."
11. Annual Revenue must be positive
AND(
NOT(ISBLANK(AnnualRevenue)),
AnnualRevenue < 0
)
Error: "Annual Revenue cannot be negative."
Status transitions (Examples 12–14)
12. Can't move Stage backward from Closed Won
AND(
ISCHANGED(StageName),
TEXT(PRIORVALUE(StageName)) = 'Closed Won',
TEXT(StageName) != 'Closed Won'
)
Error: "Once Stage is Closed Won, it cannot be moved back."
13. Closed Lost requires a reason
AND(
ISPICKVAL(StageName, 'Closed Lost'),
ISBLANK(TEXT(Loss_Reason__c))
)
Error: "Loss Reason is required when Stage is Closed Lost."
14. Case can't be Closed without resolution notes
AND(
ISPICKVAL(Status, 'Closed'),
ISBLANK(Resolution_Notes__c)
)
Error: "Resolution Notes are required to close a Case."
Cross-object (Examples 15–17)
15. Opportunity Amount can't exceed Account's annual revenue
AND(
NOT(ISBLANK(Amount)),
NOT(ISBLANK(Account.AnnualRevenue)),
Amount > Account.AnnualRevenue
)
Error: "Opportunity Amount cannot exceed the Account's Annual Revenue."
16. Contact's email domain must match account's website
AND(
NOT(ISBLANK(Email)),
NOT(ISBLANK(Account.Website)),
NOT(CONTAINS(Email,
SUBSTITUTE(SUBSTITUTE(Account.Website, 'http://', ''), 'https://', '')))
)
Error: "Contact's Email domain should match the Account's Website."
17. Quote line item count limit per opportunity (workaround using rollup)
AND(
NOT(ISBLANK(Opportunity.Total_Quote_Lines__c)),
Opportunity.Total_Quote_Lines__c >= 50
)
Error: "Cannot add more than 50 quote line items per Opportunity."
Record-type specific (Examples 18–20)
18. SLA fields required for Premium Support cases only
AND(
RecordType.DeveloperName = 'Premium_Support',
OR(ISBLANK(TEXT(SLA_Tier__c)), ISBLANK(SLA_Response_Time__c))
)
Error: "SLA Tier and Response Time are required for Premium Support cases."
19. B2C accounts must have Person Account fields populated
AND(
RecordType.DeveloperName = 'Person_Account',
OR(ISBLANK(FirstName), ISBLANK(LastName))
)
Error: "First and Last Name are required for Person Accounts."
20. Internal cases can't have customer-facing fields filled
AND(
RecordType.DeveloperName = 'Internal_Issue',
NOT(ISBLANK(Customer_Communication__c))
)
Error: "Customer-facing fields are not allowed on Internal Issues."
Error message best practices
The error message is half the rule. A bad message creates a support ticket; a good one teaches the user.
Bad: "Invalid input." Better: "Email cannot be blank." Best: "Email is required for Customers. Please add a valid email like jane@example.com."
The pattern: what's wrong + why + how to fix.
Other guidelines:
- Address the user, not the system. "You must fill in..." not "System requires..."
- Suggest a fix, not just the rule. "Use format (555) 123-4567" beats "Phone must match regex."
- Locate the message at the relevant field. Don't dump everything at the top of the page.
- Localize. If your org is multi-language, build messages with
$Label.Xreferences where possible. - Don't expose sensitive logic. "Discount > 50% requires VP approval" is fine; "VP IDs are 005xxx" is a leak.
The patterns that bite
A short list of validation-rule edge cases that catch new admins.
Pattern 1: ISCHANGED + ISNEW behavior
ISCHANGED(field) returns TRUE on insert (the value "changed" from null). If you want to validate only on edits, combine: AND(NOT(ISNEW()), ISCHANGED(field)).
Pattern 2: PRIORVALUE in test classes
PRIORVALUE returns null in some tests. If your rule depends on it, build proper Apex test class coverage that performs an update operation, not just an insert.
Pattern 3: Picklist comparisons
Always wrap picklist values in ISPICKVAL() or TEXT(). Direct equality (StageName = 'Closed Won') sometimes works in formulas but can misbehave with multi-currency or translated values.
Pattern 4: Cross-object null checks
A cross-object formula like Account.AnnualRevenue > 1000000 returns null (treated as false) when the parent isn't set. Always include NOT(ISBLANK(Account.AnnualRevenue)) first.
Pattern 5: Mass transfer skips validation
Salesforce's "Mass Transfer Records" tool skips validation rules. So does the Apex Database.update(records, false) flavor with allOrNothing=false. So do most data loaders. If your validation rule must hold even for mass operations, also enforce in Apex / a record-triggered Flow.
Pattern 6: Record-Triggered Flow can be a better fit
In 2026, complex validation often goes in a record-triggered Flow instead — easier to debug, supports multi-step logic, can call Apex sub-flows. Reserve validation rules for simple field-level invariants.
When NOT to use a validation rule
- Defaulting a field value. Use a formula field, page-layout default, or before-save Flow.
- Calculating a field from other fields. Use a formula field or Apex.
- Multi-step business logic. Use a Flow.
- Workflow-style "if X then send Y" rules. Validation rules block — they don't side-effect.
- Anything that needs to be skippable by some users. Use a permission-aware Flow that branches on user attributes.
The right validation rule is short, declarative, and "this combination is not allowed" in shape. Anything longer than ~10 lines of formula is a smell.
How Agentforce interacts with validation rules
When an agent calls an Apex Action that updates a record, validation rules fire. The agent's request fails if the rule blocks save — and the agent has to handle that error path, often by asking the user for clarification.
Two implications:
- Test agent flows with realistic data. Validation rules that fire only in edge cases will surface the first time an agent hits them in production.
- Error messages reach the agent. A clear "Email is required for Customers" message lets the agent ask the user politely. A vague "Invalid input" leaves the agent confused.
Common admin mistakes
- Stacking 20 rules on one object. They run on every save. Combine where possible.
- Long, complex formulas with no comments. Future-you will hate present-you.
- Vague error messages. "Invalid" tells nobody anything.
- Forgetting that validation rules don't enforce on integrations by default. Add Apex-level enforcement for paths that bypass them.
- Using
LEFT(Phone, 3) = '555'instead ofREGEX. REGEX is more readable and handles edge cases.
Frequently asked questions
Can a validation rule fire on a delete? No — validation rules fire on insert and update only. Use a trigger for delete-blocking.
Can I disable a validation rule for a specific user?
Yes — add a check like NOT($Permission.Bypass_Validation__c) and grant the custom permission to specific users via Permission Sets.
Do validation rules slow down saves? Negligibly — they're evaluated as formulas. The only time they bite is if you have hundreds firing on bulk DML, where each evaluation adds CPU.
Can validation rules call Apex? No — validation rules are formula-only. For logic that needs Apex, use a record-triggered Flow with an invocable Apex method, or write the logic directly in a Trigger.
How do I see which validation rules fired? Salesforce returns the failure as part of the save error. The standard UI shows the message; in the API, the response includes the rule name. For tracing, enable debug logs on the running user.
What to read next
- Validation Rule, Record Type — the dictionary entries.
- Record-Triggered Flows — the 2026 alternative for complex validation.
- Apex Trigger Framework — when to escalate validation to Apex.
If you only adopt one habit: write the error message first. Decide what the user needs to know. Then build the formula that triggers it. The rule that comes out of that order is always better than the one that starts with the formula.
Share this article
Sources
Related dictionary terms
Keep reading

Salesforce Data Model Explained: Objects, Records, Fields & Relationships (Beginner's Guide)
The complete beginner's guide to the Salesforce data model — objects, fields, all six relationship types, junction objects, record types, and Schema Builder. Worked examples included.

The Complete 2026 Guide to Record-Triggered Flows in Salesforce
Record-triggered flows are the Salesforce automation default in 2026. This is the complete tutorial — before-save, after-save, scheduled paths, gotchas, and 5 worked examples.
