BR-E-08Exempt VAT breakdown with nothing exempt on the invoice

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-08.

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-08]-In a VAT breakdown (BG-23) where the VAT category code (BT-118) is " Exempt from VAT" the VAT category taxable amount (BT-116) shall equal the sum of Invoice line net amounts (BT-131) minus the sum of Document level allowance amounts (BT-92) plus the sum of Document level charge amounts (BT-99) where the VAT category codes (BT-151, BT-95, BT-102) are "Exempt from VAT".

Also cited in this report

  • BR-CO-17

    [BR-CO-17]-VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.

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 VAT breakdown with nothing exempt on the invoice

Reported ascategory_unuseda breakdown for a category nothing on the invoice uses

What is wrong

The invoice's VAT breakdown has a cac:TaxSubtotal coded E - exempt from VAT - under the VAT scheme, but no line item and no document-level allowance or charge is coded E under the VAT scheme. EN 16931 rule BR-E-08, which ZATCA's checker runs since SDK 3.4.8, checks that the exempt taxable amount equals the exempt lines less allowances plus charges, and reports a breakdown with nothing to add up. ZATCA flags this as a warning, which does not by itself reject the document.

What to check in your ERP

Either the breakdown should not be there, or the lines it summarises are coded with another category. Check that the category code on each line item (cac:ClassifiedTaxCategory/cbc:ID), and its tax scheme, match the breakdown the line is totalled into; a breakdown left at E while the line was recoded as zero-rated is the usual cause.

Exempt VAT breakdown states no taxable amount

Reported astaxable_amount_missingthe taxable amount is missing

What is wrong

A cac:TaxSubtotal directly under the invoice's own cac:TaxTotal is coded E - exempt from VAT - under the VAT tax scheme and carries no cbc:TaxableAmount. EN 16931 rule BR-E-08, which ZATCA's checker runs since SDK 3.4.8, compares that amount against the sum of the exempt line amounts, plus exempt charges, minus exempt allowances. An absent element is an empty sequence in XPath: arithmetic over it yields the empty sequence and every comparison against it is false, so the rule fails on absence rather than passing over it, and the amount has to be written out even when it is nought. ZATCA flags this as a warning, which does not by itself reject the document. Two things about the sums, because they are narrower than the words suggest: they are taken from the direct children of the document root only, so an allowance attached to an invoice line is not in them; and the contributing lines, charges and allowances are picked up by the category letter alone, with no test that the tax scheme is VAT.

What to check in your ERP

The usual cause is an export that suppresses amount elements whose value is nought, and the exempt group is one it drops, because a breakdown rebuilt from posted tax lines has no tax line for a supply that carried no tax. Write cbc:TaxableAmount explicitly, formatted to two decimals and carrying the same currencyID attribute as the other amounts. If the group has elements but no figures, look at the per-category tax summary rather than at the export template. Expect the same suppression to be affecting your zero-rated and out-of-scope breakdowns, so fix it once.

Exempt VAT breakdown's taxable amount does not match its lines

Reported astaxable_amount_mismatchthe taxable amount does not match the lines

What is wrong

A cac:TaxSubtotal directly under the invoice's own cac:TaxTotal is coded E - exempt from VAT - under the VAT tax scheme, and its cbc:TaxableAmount does not equal the sum of the exempt line amounts, plus exempt charges, minus exempt allowances. EN 16931 rule BR-E-08, which ZATCA's checker runs since SDK 3.4.8, compares the two exactly: there is no tolerance and no rounding step, unlike BR-S-09 and BR-CO-17, which allow a band of one currency unit around a category's VAT amount. A difference of one halalah fails this rule. ZATCA flags it as a warning, which does not by itself reject the document. The test expression ZATCA's stylesheet records for this rule names the standard-rated category, by a copying slip; the check ZATCA runs adds up the exempt ones, and this note follows the check. Two things about the sums, because they are narrower than the words suggest: they are taken from the direct children of the document root only, so an allowance attached to an invoice line is not in them; and the contributing lines, charges and allowances are picked up by the category letter alone, with no test that the tax scheme is VAT.

What to check in your ERP

Rebuild the figure by hand from the exported file: add up cbc:LineExtensionAmount on every line whose category is E, add the document-level charges coded E, subtract the document-level allowances coded E, and compare the result with the breakdown's cbc:TaxableAmount. The frequent causes are the exempt taxable amount taken from the document's tax-exclusive total instead of from the exempt group, a document-level allowance whose own cac:TaxCategory carries a different letter so the rule does not subtract it, a discount applied to the breakdown but not to the line's cbc:LineExtensionAmount, and rounding applied to the breakdown independently of the lines. Start at the per-category tax summary, which is where a breakdown and its lines stop agreeing.

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

count(/*/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[normalize-space(cbc:ID) = 'E'][cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT'])> 0 and not(exists(../../../cac:AllowanceCharge/cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) or exists(../../../cac:InvoiceLine[cac:Item/cac:ClassifiedTaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']])) or not( (xs:decimal(../cbc:TaxableAmount) =(sum(../../../cac:InvoiceLine[cac:Item/cac:ClassifiedTaxCategory/normalize-space(cbc:ID)='E']/xs:decimal(cbc:LineExtensionAmount)) + sum(../../../cac:AllowanceCharge[cbc:ChargeIndicator=true()][cac:TaxCategory/normalize-space(cbc:ID)='E']/xs:decimal(cbc:Amount)) - sum(../../../cac:AllowanceCharge[cbc:ChargeIndicator=false()][cac:TaxCategory/normalize-space(cbc:ID)='E']/xs:decimal(cbc:Amount)) )))

Reported test

count(/*/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[normalize-space(cbc:ID) = 'E'][cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT'])> 0 and (exists(../../../cac:AllowanceCharge/cac:TaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']) or exists(../../../cac:InvoiceLine[cac:Item/cac:ClassifiedTaxCategory[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']/cbc:ID[normalize-space(.) = 'E']])) or ((xs:decimal(../cbc:TaxableAmount) =(sum(../../../cac:InvoiceLine[cac:Item/cac:ClassifiedTaxCategory/normalize-space(cbc:ID)='S']/xs:decimal(cbc:LineExtensionAmount)) + sum(../../../cac:AllowanceCharge[cbc:ChargeIndicator=true()][cac:TaxCategory/normalize-space(cbc:ID)='S']/xs:decimal(cbc:Amount)) - sum(../../../cac:AllowanceCharge[cbc:ChargeIndicator=false()][cac:TaxCategory/normalize-space(cbc:ID)='S']/xs:decimal(cbc:Amount)) )))
Evaluated against
/*/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[normalize-space(cbc:ID) = 'E'][cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']
Source
vendor/zatca/schematron/CEN-EN16931-UBL.xsl:1338

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-115-e-taxable-amount-off-by-hundredth.xml
Mutabiq's verdict
Passed
Reported as
taxable_amount_mismatchthe taxable amount does not match the lines

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.