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.
Zero-rated VAT breakdown gives no exemption reason code
Reported asreason_code_missingthe reason code is missing
What is wrong
A cac:TaxSubtotal directly under the invoice's own cac:TaxTotal is coded Z - zero-rated - under the VAT tax scheme, and its cac:TaxCategory carries no cbc:TaxExemptionReasonCode. SDK 3.4.8 flags the document under BR-KSA-69 as a warning, not a rejection: the finding is reported without failing the document by itself. Earlier SDK releases (3.0.8) required the reason TEXT as well as the code; SDK 3.4.8 asks for the code only, so a zero-rated breakdown that carries the reason text but no code still draws this warning, and the retired rule that once accepted either one on its own, BR-E-10, no longer exists in SDK 3.4.8. BR-KSA-69 is mirrored beside two siblings the pack added when it moved to SDK 3.4.8: BR-KSA-23 asks the same question of an EXEMPT ('E') breakdown, and BR-KSA-24 of an OUT-OF-SCOPE ('O') one, both also at warning. BR-KSA-CL-04, which checks the same code against ZATCA's published list for its category, stays silent on a code that is absent or blank in SDK 3.4.8, so this note is the one that reports it.
What to check in your ERP
The code belongs in cac:TaxSubtotal/cac:TaxCategory/cbc:TaxExemptionReasonCode, beside the text your document already carries. The usual cause is an export built to the EN 16931 profile, where a reason text on its own is enough, so the code was never mapped; the second is a system with one reason field, which the text has taken. Add the code to every zero-rated tax code in your VAT code table. It has to be one of the VATEX-SA values ZATCA publishes for zero-rated supplies, which BR-KSA-CL-04 checks separately, so settle which one applies with whoever owns the VAT code table before mapping it - that is a tax question rather than a mapping one.
Zero-rated VAT breakdown's exemption reason code is empty
Reported asreason_code_emptythe reason code is present but empty
What is wrong
A cac:TaxSubtotal directly under the invoice's own cac:TaxTotal is coded Z - zero-rated - under the VAT tax scheme, and its cac:TaxCategory carries a cbc:TaxExemptionReasonCode with nothing in it once whitespace is removed. SDK 3.4.8 flags the document under BR-KSA-69 as a warning, not a rejection: the finding is reported without failing the document by itself. The rule requires the code to exist and to have a value, and the second of those is what failed. The value is measured after normalize-space, so a code made only of spaces counts as empty and draws this note. BR-KSA-CL-04, which checks the code against ZATCA's published list for its category, stays silent on a blank code, so this note is the one that reports it. SDK 3.0.8 also required the reason text and measured the code without normalising it; SDK 3.4.8 asks for the code only (KSA:2592).
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 code held as an empty string, or as spaces only, on the zero-rated 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 the exempt codes have the field filled. Fill it with the VATEX-SA value for the article the supply falls under, which is what BR-KSA-CL-04 checks; a placeholder or an internal code clears this note and draws that one.
Zero-rated VAT breakdown gives no exemption reason text
Reported asreason_missingthe reason text is missing
What is wrong
A zero-rated (Z) VAT breakdown carries no cbc:TaxExemptionReason, the VAT exemption reason text (BT-120). Only rule packs before 2.0.0 report this: SDK 3.0.8's BR-KSA-69 required a zero-rated breakdown to carry the reason text as well as the reason code, and SDK 3.4.8, which rule pack 2.0.0 reads, asks for the code only (KSA:2592), so a report produced under 2.0.0 never carries this note. What 3.4.8 still reports on the same breakdown is a missing or blank reason code, under this rule at warning, and a code not on the published list for its category, under BR-KSA-CL-04.
What to check in your ERP
Nothing needs adding for rule pack 2.0.0 on this account: the reason text is not required under SDK 3.4.8. Check the code beside it instead, in cac:TaxSubtotal/cac:TaxCategory/cbc:TaxExemptionReasonCode, on every zero-rated tax code in your VAT code table: it has to be present and one of the VATEX-SA values ZATCA publishes for zero-rated supplies.
Zero-rated VAT breakdown's exemption reason text is empty
Reported asreason_emptythe reason text is present but empty
What is wrong
A zero-rated (Z) VAT breakdown carries a cbc:TaxExemptionReason, the VAT exemption reason text (BT-120), with nothing in it. Only rule packs before 2.0.0 report this: SDK 3.0.8's BR-KSA-69 required a zero-rated breakdown to carry the reason text as well as the reason code, and SDK 3.4.8, which rule pack 2.0.0 reads, asks for the code only (KSA:2592), so a report produced under 2.0.0 never carries this note. What 3.4.8 still reports on the same breakdown is a missing or blank reason code, under this rule at warning, and a code not on the published list for its category, under BR-KSA-CL-04.
What to check in your ERP
Either give the reason text a value, the published wording for the article the supply falls under, or leave the element out; an element written with nothing in it is not what to send. Then check the code beside it, in cbc:TaxExemptionReasonCode, which SDK 3.4.8 does require: present, and one of the VATEX-SA values ZATCA publishes for zero-rated supplies.
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:TaxExemptionReasonCode) and normalize-space(cbc:TaxExemptionReasonCode )!='' )
Reported test
not((exists(cbc:TaxExemptionReasonCode) ) and (contains('VATEX-SA-32 VATEX-SA-33 VATEX-SA-34-1 VATEX-SA-34-2 VATEX-SA-34-3 VATEX-SA-34-4 VATEX-SA-34-5 VATEX-SA-35 VATEX-SA-36 VATEX-SA-EDU VATEX-SA-HEA VATEX-SA-MLTRY VATEX-SA-DIPLOMAT',cbc:TaxExemptionReasonCode)) )
- 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:2593As 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.
© 2026 Hayatiq AI Solutions F.Z.E. All rights reserved.