BR-E-01Exempt supplies are on the invoice but there is no exempt VAT breakdown

Mutabiq checks Saudi ZATCA Phase 2 e-invoice XML before you submit it: pre-validation against published ZATCA requirements, not an approval by the authority. This page explains one rule, BR-E-01.

Validate 5 invoices free

Five checks a month on the Free plan. The file is checked in memory and is never stored.

The message ZATCA prints

[BR-E-01]-An Invoice that contains an Invoice line (BG-25), a Document level allowance (BG-20) or a Document level charge (BG-21) where the VAT category code (BT-151 or BT-95 or BT-102) is "Exempt from VAT" shall contain at least one VAT breakdown (BG-23) with the VAT category code (BT-118) equal to "Exempt from VAT".

What Mutabiq reports

One entry for each way this rule can be reported.

Reported as
What kind of value was found — empty, not_15_digits and the like — never the value itself.

Exempt supplies are on the invoice but there is no exempt VAT breakdown

Reported asbreakdown_missingthe VAT breakdown is missing

What is wrong

The exempt category code E is used in the document under the VAT tax scheme - on an invoice line's cac:ClassifiedTaxCategory, or on the cac:TaxCategory of an allowance or a charge - and there is no cac:TaxSubtotal for it directly under the invoice's own cac:TaxTotal. EN 16931 rule BR-E-01, which ZATCA runs, requires at least one exempt breakdown on any invoice that uses the category. SDK 3.4.8 flags it as a warning, not a rejection: the finding is reported without failing the document by itself (EN:790). The count is of breakdowns directly under the document root, so a cac:TaxSubtotal written inside an invoice line does not satisfy it. SDK 3.0.8 asked for exactly one breakdown, at error, and counted a breakdown's own category as a use of the category; SDK 3.4.8 asks for at least one, and only a line, an allowance or a charge in the category triggers the rule.

What to check in your ERP

The VAT breakdown must carry one cac:TaxSubtotal per category used, and an exempt category still needs its block: cbc:TaxableAmount, a cbc:TaxAmount of zero, the category code E, and an exemption reason code from ZATCA's exempt list, VATEX-SA-29, VATEX-SA-29-7 or VATEX-SA-30, which ZATCA's own rules ask of every exempt breakdown. The frequent cause is an export that builds the breakdown from the posted tax lines, which have no rows for an exempt supply. Build it from the per-category summary instead.

More than one exempt VAT breakdown on the same invoice

Reported asbreakdown_duplicatedthe same VAT breakdown appears more than once

What is wrong

The invoice carries more than one cac:TaxSubtotal coded E - exempt - under the VAT tax scheme directly under its own cac:TaxTotal. Only rule packs before 2.0.0 report this: SDK 3.0.8's BR-E-01 required exactly one breakdown per category, and SDK 3.4.8, which rule pack 2.0.0 reads, requires at least one (EN:790), so a report produced under 2.0.0 never carries this note. EN 16931 still describes the VAT breakdown as one block per category rather than one per exemption reason.

What to check in your ERP

Nothing needs changing for rule pack 2.0.0 on this account. If you do merge the blocks, group by the category letter alone rather than by the exemption reason or its code, sum the taxable amounts into one cac:TaxSubtotal, and settle on the single reason code that block will carry.

The clause that runs

Quoted from the validation-rule stylesheet ZATCA publishes, at the file and line named below. The executed guard is what the check runs; the reported test is what it prints when it fails, and the two are not always the same.

Executed guard

not(((exists(//cac:AllowanceCharge/cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) or exists(//cac:ClassifiedTaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E'])) and (count(cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) > 0)) or (not(//cac:AllowanceCharge/cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) and not(//cac:ClassifiedTaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E'])))

Reported test

((exists(//cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) or exists(//cac:ClassifiedTaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E'])) and (count(cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) = 1)) or (not(//cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) and not(//cac:ClassifiedTaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']))
Evaluated against
/ubl:Invoice | /cn:CreditNote
Source
vendor/zatca/schematron/CEN-EN16931-UBL.xsl:791

As shipped in ZATCA SDK 3.4.8: the copy quoted here is, byte for byte, the file that release ships. The date in the file name is the name ZATCA gives the file; the rules in it are those of SDK 3.4.8.

A worked example

A synthetic document from the measured test corpus that trips this rule, shown where it differs from one that passed. Mutabiq's report names the class of the value, never the value itself.

Document
bad-66-e01-no-exempt-breakdown.xml
Mutabiq's verdict
Passed
Reported as
breakdown_missingthe VAT breakdown is missing

Download this example (.xml)Check it in Mutabiq

This page's example, checked by the same rules your own upload goes through. No upload and no sign-in.

Rules evaluated against the same part of the document. One wrong value often reports several of them at once.

Check an invoice

Check your own invoice

© 2026 Hayatiq AI Solutions F.Z.E. All rights reserved.