BR-KSA-83Exemption reason code without the reason text beside it

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-KSA-83.

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-KSA-83]-A VAT breakdown (BG-23) with VAT Category code (BT-118) 'Exempt from VAT' or 'Zero rated' or 'Not subject to VAT' shall have a VAT exemption (or exception) reason text (BT-120) with one of the values from paragraph 11.2.4 of XML implementation standards on Tax exemption (or exception) reason text - specific to Saudi Arabia.

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.

Exemption reason code without the reason text beside it

Reported asreason_text_missingthe reason text is missing beside its code

What is wrong

A VAT breakdown directly under the invoice's own cac:TaxTotal is coded Z, E or O - zero-rated, exempt or not subject to VAT - and its cac:TaxCategory carries a cbc:TaxExemptionReasonCode but no cbc:TaxExemptionReason, the exemption reason text (BT-120). SDK 3.4.8 flags the document under BR-KSA-83 as a warning, not a rejection: the finding is reported without failing the document by itself. The rule's own message promises a check of the text against the published list in paragraph 11.2.4 of the XML implementation standard, but the part of the rule that would do so is disabled in the files ZATCA ships, and what runs asks only that a text exist and not be blank; no list is checked, by ZATCA or here. The rule that once did, BR-KSA-CL-05, no longer exists in SDK 3.4.8.

What to check in your ERP

The text belongs in cac:TaxSubtotal/cac:TaxCategory/cbc:TaxExemptionReason, beside the code the document already carries. Map the published wording for the article the supply falls under - the VATEX-SA code table ZATCA publishes gives one sentence per code - from the same VAT code table the code comes from, so the two cannot drift apart. A system with one reason field, which the code has taken, is the usual cause; an export built to the EN 16931 profile, where the code alone is enough, is the other.

Exemption reason text is blank beside its code

Reported asreason_text_blankthe reason text is present but blank

What is wrong

A VAT breakdown directly under the invoice's own cac:TaxTotal is coded Z, E or O - zero-rated, exempt or not subject to VAT - and its cac:TaxCategory carries a cbc:TaxExemptionReasonCode and a cbc:TaxExemptionReason with nothing in it once whitespace is removed. SDK 3.4.8 flags the document under BR-KSA-83 exactly as it does a missing text: as a warning, not a rejection, so the finding is reported without failing the document by itself. The value is measured after normalize-space, so a text made only of spaces counts as blank; the separate length rule, BR-KSA-F-06-C16, reads the raw value and is satisfied by the spaces, which is why only this note appears. No list of texts is checked - the part of the rule that would do so is disabled in the files ZATCA ships.

What to check in your ERP

The element is being written and the value is not, which points at the value the mapping reads rather than at the template. Look for an exemption reason text held as an empty string, or as spaces only, on the zero-rated, exempt and out-of-scope tax codes, for a lookup that returns nothing rather than a default when it misses, and for a template that writes the element for every breakdown while only some codes have the field filled. Fill it with the published wording for the code, or do not write the element at all; an element written with nothing in it is not what to send.

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(cbc:TaxExemptionReason) and normalize-space(cbc:TaxExemptionReason))

Reported test

exists(cbc:TaxExemptionReason)
Evaluated against
ubl:Invoice/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[normalize-space(cbc:ID) = 'Z'] |ubl:Invoice/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[normalize-space(cbc:ID) = 'E'] | ubl:Invoice/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory[normalize-space(cbc:ID) = 'O']
Source
vendor/zatca/schematron/20210819_ZATCA_E-invoice_Validation_Rules.xsl:2552

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-116-zero-rated-blank-reason-text.xml
Mutabiq's verdict
Passed
Reported as
reason_text_blankthe reason text is present but blank

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.