Document Purpose: Guide to validation mechanisms in XBRL
Last Updated: January 2026
This document covers validation approaches in XBRL, from basic XBRL 2.1 validation through advanced Formula specifications.
XBRL provides multiple layers of validation:
┌────────────────────────────────────────────────┐
│ Layer 5: Custom Business Rules (Formula) │
├────────────────────────────────────────────────┤
│ Layer 4: Cross-Fact Validation (Calculations) │
├────────────────────────────────────────────────┤
│ Layer 3: Semantic Validation (XBRL 2.1) │
├────────────────────────────────────────────────┤
│ Layer 2: Schema Validation (XSD) │
├────────────────────────────────────────────────┤
│ Layer 1: Syntax Validation (XML) │
└────────────────────────────────────────────────┘
What It Checks:
How:
Standard XML parsers handle this automatically.
Errors:
What It Checks:
Example:
<!-- Schema requires monetaryItemType -->
<ex:Revenue contextRef="c1" unitRef="usd">1000000</ex:Revenue> <!-- ✓ Valid -->
<ex:Revenue contextRef="c1" unitRef="usd">ABC</ex:Revenue> <!-- ✗ Invalid: not numeric -->
Validation Rules:
Unique Context Rule:
Two contexts are the same if they have identical:
Error: Duplicate contexts should be merged (warning, not error).
Complete Tuple Rule:
Two tuples are complete duplicates if all child facts are identical.
Error: Complete duplicate tuples not allowed.
Fact Consistency Rule:
Two non-tuple facts with same concept + context + unit must have same value.
Error: Inconsistent facts (unless in different tuples).
Rule:
Facts must use period type matching concept's periodType attribute.
<!-- Concept definition -->
<xs:element name="Revenue" periodType="duration" .../>
<!-- Valid instance fact -->
<ex:Revenue contextRef="duration_context" ...>1000000</ex:Revenue>
<!-- Invalid instance fact -->
<ex:Revenue contextRef="instant_context" ...>1000000</ex:Revenue>
<!-- Error: Revenue requires duration period, got instant -->
Numeric Items:
Non-Numeric Items:
Rule:
decimals or precision required for numeric factsINF<ex:Revenue decimals="0" unitRef="usd" ...>1000000</ex:Revenue> <!-- ✓ -->
<ex:Revenue precision="7" unitRef="usd" ...>1000000</ex:Revenue> <!-- ✓ -->
<ex:Revenue decimals="0" precision="7" unitRef="usd" ...>1000000</ex:Revenue> <!-- ✗ -->
Concept:
Calculation linkbase defines summation relationships.
Validation Rule:
Parent = Σ(Children × Weight) ± Tolerance
Tolerance Calculation:
Based on decimals/precision of parent fact.
Example:
Taxonomy calculation:
GrossProfit (parent)
├─ Revenue (weight: 1, order: 1)
└─ CostOfSales (weight: -1, order: 2)
Instance facts:
<ex:Revenue contextRef="c1" unitRef="usd" decimals="-3">1000000</ex:Revenue>
<ex:CostOfSales contextRef="c1" unitRef="usd" decimals="-3">600000</ex:CostOfSales>
<ex:GrossProfit contextRef="c1" unitRef="usd" decimals="-3">400000</ex:GrossProfit>
Validation:
GrossProfit = Revenue × 1 + CostOfSales × (-1)
400000 = 1000000 + (-600000)
400000 = 400000 ✓
Tolerance:
With decimals="-3", tolerance is ±500 (rounding to thousands).
Cross-Context Rule:
Cannot calculate across different period types (instant vs duration).
Cross-Dimension Rule:
Calculations only valid within same dimensional context.
Missing Facts:
If any summand is missing, calculation not tested.
Enhancements:
Status: Recommendation 2018, updates 2025.
Status: Recommendation (REC)
Released: October 2011
Comprehensive framework for expressing complex business rules.
┌──────────────────────────────────────┐
│ Formula Core │
│ (Base specification) │
└──────────────────────────────────────┘
↓ uses
┌──────────────────────────────────────┐
│ Variables │
│ (Fact selection and binding) │
└──────────────────────────────────────┘
↓ filtered by
┌──────────────────────────────────────┐
│ Filters │
│ (Concept, dimension, value filters) │
└──────────────────────────────────────┘
↓ validates with
┌──────────────────────────────────────┐
│ Assertions │
│ (Value, Existence, Consistency) │
└──────────────────────────────────────┘
Variables select and bind facts from instance.
Types:
Example:
<variable:factVariable name="revenueVar" bindAsSequence="false">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:Revenue</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
This selects all Revenue facts.
Filters refine fact selection.
Filter Types:
Example - Complex Filter:
<variable:filter>
<bf:and>
<cf:conceptName>
<cf:qname>ex:Revenue</cf:qname>
</cf:conceptName>
<df:explicitDimension>
<df:dimension>
<df:qname>ex:RegionDimension</df:qname>
</df:dimension>
<df:member>
<df:qname>ex:NorthAmerica</df:qname>
</df:member>
</df:explicitDimension>
</bf:and>
</variable:filter>
This selects Revenue facts for North America.
Assertions test business rules.
Test if fact value meets condition.
Example:
<va:valueAssertion test="$revenue gt 0" id="va1">
<formula:aspects source="xbrl.instance">
<formula:concept>
<formula:qname>ex:Revenue</formula:qname>
</formula:concept>
</formula:aspects>
<variable:factVariable name="revenue" bindAsSequence="false">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:Revenue</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
<msg:message>
Revenue must be positive. Found: {$revenue}
</msg:message>
</va:valueAssertion>
XPath test expression:
$revenue gt 0 - Revenue must be greater than zeroResult:
Test if facts exist.
Example:
<ea:existenceAssertion test="count($revenue) eq 1" id="ea1">
<variable:factVariable name="revenue" bindAsSequence="true">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:Revenue</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
<msg:message>
Exactly one Revenue fact required. Found: {count($revenue)}
</msg:message>
</ea:existenceAssertion>
Use Cases:
Test relationships between facts.
Example:
<ca:consistencyAssertion test="$grossProfit eq ($revenue - $costs)" id="ca1">
<variable:factVariable name="revenue" bindAsSequence="false">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:Revenue</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
<variable:factVariable name="costs" bindAsSequence="false">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:CostOfSales</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
<variable:factVariable name="grossProfit" bindAsSequence="false">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:GrossProfit</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
<msg:message>
Gross Profit must equal Revenue minus Costs.
</msg:message>
</ca:consistencyAssertion>
Use Cases:
Formulas can create new facts.
Example:
<formula:formula value="$revenue * $taxRate" id="f1" decimals="0">
<formula:aspects source="xbrl.instance">
<formula:concept>
<formula:qname>ex:TaxExpense</formula:qname>
</formula:concept>
</formula:aspects>
<variable:factVariable name="revenue" bindAsSequence="false">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:Revenue</cf:qname>
</cf:conceptName>
</variable:filter>
</variable:factVariable>
<variable:generalVariable name="taxRate">
<variable:expression>0.21</variable:expression>
</variable:generalVariable>
</formula:formula>
Output:
New fact: ex:TaxExpense with computed value.
Use Cases:
Assertions can have different severity levels:
<formula:assertionSet>
<formula:assertion id="va1"
severity:severity="http://xbrl.org/2016/assertion-severity-error"/>
<formula:assertion id="va2"
severity:severity="http://xbrl.org/2016/assertion-severity-warning"/>
<formula:assertion id="va3"
severity:severity="http://xbrl.org/2016/assertion-severity-ok"/>
</formula:assertionSet>
Severity Levels:
Customizable validation messages:
<msg:message xml:lang="en">
Revenue {$revenue} must be positive.
Period: {$period}
Entity: {$entity}
</msg:message>
<msg:message xml:lang="fr">
Le revenu {$revenue} doit être positif.
</msg:message>
Features:
Extend XPath with custom functions:
<function:function name="custom:calculateRatio" output="xs:decimal">
<function:input name="numerator" type="xs:decimal"/>
<function:input name="denominator" type="xs:decimal"/>
<function:implementation>
if ($denominator eq 0) then 0 else $numerator div $denominator
</function:implementation>
</function:function>
Use in assertions:
<va:valueAssertion test="custom:calculateRatio($assets, $liabilities) gt 1.5">
...
</va:valueAssertion>
Registry:
Shared functions can be registered at:
https://www.xbrl.org/registries/functions-registry/
1. Parse Instance
↓
2. Load Taxonomy
↓
3. XML Syntax Validation
↓
4. Schema Validation
↓
5. XBRL 2.1 Semantic Validation
↓
6. Dimension Validation (if using Dimensions)
↓
7. Calculation Validation (if calculation linkbase present)
↓
8. Formula Validation (if formula linkbase present)
↓
9. Generate Validation Report
<validation:report>
<validation:error code="xbrl:factDuplicate"
fact="f1"
severity="error">
Duplicate fact detected: Revenue for period 2024
</validation:error>
<validation:warning code="calc:summationInconsistent"
fact="f5"
severity="warning">
Calculation inconsistent: GrossProfit calculation failed
Expected: 400000, Actual: 399999, Tolerance: 500
</validation:warning>
<validation:info code="formula:assertionSatisfied"
assertion="va1"
severity="ok">
Value assertion satisfied: Revenue is positive
</validation:info>
</validation:report>
Implement validation in layers, catching syntax errors before semantic errors.
Provide context:
Validate as you parse when possible.
Saxon (XPath processor)
Schematron (Alternative)
Arelle
<ca:consistencyAssertion test="$assets eq ($liabilities + $equity)">
<variable:factVariable name="assets" .../>
<variable:factVariable name="liabilities" .../>
<variable:factVariable name="equity" .../>
<msg:message>
Assets must equal Liabilities plus Equity.
Assets: {$assets}
Liabilities: {$liabilities}
Equity: {$equity}
</msg:message>
</ca:consistencyAssertion>
<va:valueAssertion test="$startDate lt $endDate">
<variable:generalVariable name="startDate">
<variable:expression>
xfi:period-start($period)
</variable:expression>
</variable:generalVariable>
<variable:generalVariable name="endDate">
<variable:expression>
xfi:period-end($period)
</variable:expression>
</variable:generalVariable>
<msg:message>
Period start date must be before end date.
</msg:message>
</va:valueAssertion>
<ea:existenceAssertion test="exists($disclosure)">
<variable:factVariable name="disclosure">
<variable:filter>
<cf:conceptName>
<cf:qname>ex:MaterialContingency</cf:qname>
</cf:conceptName>
</variable:filter>
<!-- Only when revenue > threshold -->
<formula:implicitFiltering>
if ($revenue gt 10000000) then true() else false()
</formula:implicitFiltering>
</variable:factVariable>
<variable:factVariable name="revenue" .../>
<msg:message>
Material contingency disclosure required when revenue exceeds $10M.
</msg:message>
</ea:existenceAssertion>
| Layer | What | Specification | Complexity |
|---|---|---|---|
| 1 | XML Syntax | XML | Low |
| 2 | Schema | XSD | Low |
| 3 | XBRL Semantics | XBRL 2.1 | Medium |
| 4 | Calculations | XBRL 2.1, Calc 2.0 | Medium |
| 5 | Business Rules | Formula | High |
XBRL 2.1 Validation:
Calculations:
Formula: