BR-KSA-DEC-02 Rejects the invoice

VAT rate out of range, or carrying more than two decimals

The rate is a percentage, not a fraction: fifteen per cent is written 15, never 0.15. The permitted window is 0.00 to 100.00 with at most two decimals, numerals only and no percent sign — so a rate recovered by division and left unrounded fails here just as surely as a value written with a symbol.

The message text as the Authority sends it in English, unedited

The VAT rates (BT-96, BT-103, BT-119, BT-152, KSA-34) must be from 0.00 to 100.00, with maximum two decimals.Only numerals are accepted, the percentage symbol (%) is not allowed.

What does this mean?

Five fields carry a rate, and the rule covers all of them: the document level allowance VAT rate (BT-96), the document level charge VAT rate (BT-103), the VAT category rate on each subtotal (BT-119), the invoiced item VAT rate on each line (BT-152), and the prepayment VAT rate (KSA-34). A document can therefore be right on its lines and wrong on a subtotal or on its discount.

And here is the point to read carefully before you go hunting in the wrong place: 0.15 does not fail this rule. It is inside the window and its two decimals are fine — but it means fifteen hundredths of one per cent, not fifteen per cent. So the file passes here and then fails BR-KSA-84, an error, which limits a standard-rated S line to 5 or 15. It also fails the arithmetic, because the VAT amount no longer equals the base times the rate divided by one hundred. Writing the fraction is a real mistake — it is simply not the mistake this code catches.

What does fail here is four things: a value above 100.00 — a rate multiplied by a hundred twice, so 1500; a negative value — a credit note produced by negating every number, when a rate is not a quantity to negate; more than two decimals — 14.999999999999998 out of floating-point arithmetic, or a rate recovered by dividing the VAT amount by the base and left unrounded; and a value that is not purely a number, which the message states outright: the percent symbol is not allowed.

Numerals only means numerals only: a comma as the decimal separator, a space, or Arabic-Indic digits are all risks from a localised formatter running before the XML is written. The safe form is Latin digits and a dot. Both 15 and 15.00 are within two decimals, so trailing zeros do no harm.

Two rules narrow this wide window down to what you actually see on a Saudi invoice: BR-KSA-84 limits a standard-rated S line to 5 or 15, and BR-KSA-11 requires zero on a line not subject to VAT. The letter that decides which is the tax category in BR-KSA-18. So the practical set of values is three — 15, 5 and 0 — and this rule is the outer fence, which you only hit through an arithmetic or a formatting fault.

Why does it happen?

Ordered from the most common to the least — your cause is most likely the first or the second.

  • The rate stored as a fraction (0.15) in a system built for a library that expects fractions, and then passed through unchanged.
  • A rate recovered by division: the VAT amount over the base times a hundred, computed on already-rounded figures, giving 14.999999999999998 or 15.000000000000002.
  • Floating-point arithmetic serialised at full precision instead of being rounded at the moment it is written.
  • A formatted string reaching the XML as it stands: 15%, 15,00, or a rate in Arabic-Indic digits.
  • A negative rate on a credit note produced by negating every number on the source invoice, taking the rate down with the amounts.
  • A rate scaled by a hundred twice: once in the source system and once in the mapping.
  • A free rate field in an ERP where someone typed 1.5 or 150 by mistake.

How to fix it

3 steps, then send the invoice again.

  1. 1
    Store the rate as a percentage and round it where it is written

    15 means fifteen per cent, so do not store it as a fraction and rely on a conversion. And round to two decimals at the moment the value is written into the XML, not several steps earlier: floating point leaks through any earlier rounding and reappears at serialisation.

  2. 2
    Derive the rate from the category, not the other way round

    The letter fixes the number: S is 15 (or 5 where that applies), and Z, E and O are 0. A fixed table from category to rate closes this door entirely, and closes BR-KSA-11 and BR-KSA-84 in the same pass. The four letters and what each requires are in BR-KSA-18.

  3. 3
    And check every place a rate appears

    Not the lines alone: the tax subtotals (BT-119), any document level allowance or charge (BT-96 and BT-103), and a prepayment (KSA-34). And if the rate was wrong then the amounts computed from it are wrong too, which is a different family of codes appearing in the same response — how to read the whole response is in ZATCA error codes explained.

Once corrected, send the invoice again. A rejected invoice was never recorded with the Authority, so it needs no credit note — send the corrected invoice itself.

Does ZATCA Tools prevent it?

Yes, and there is no path to a wrong rate anywhere on the route, because the rate is never accepted from outside. The values are four constants in TaxCategories: 15.0 for S, and zero for Z, E and O. TaxCategories::rate() is the only source of the percent written into the XML, and it is read in exactly three places: the tax category on a line, each tax subtotal, and the category on a document level allowance. No door in the product takes a rate — not the invoice form, not the API, not the store integrations: a caller names the letter, and a letter that is not one of the four is coerced to S. And there is no division on that path at all: the amount is derived from the rate — line net times rate divided by one hundred — never the rate from the amount, and it is that reverse division that manufactures the extra decimals elsewhere.

Related codes

Frequently asked questions

I send 0.15. Is that what this rejected? +
Probably not. 0.15 is inside the window and has two decimals, so it passes this rule and fails elsewhere: BR-KSA-84 if the line is standard-rated, and the agreement between the rate and the VAT amount. Write 15 regardless — but the code that caught you is not this one, so keep reading the rest of the error list.
My rate is 14.999999999999998. Where did that come from? +
From recovering it by division instead of taking it from the category. That is the shape that fails this rule specifically: more than two decimal places. Take the rate from the category table and leave the division to computing the amounts.
Is a rate of 5 allowed? +
BR-KSA-84 names 5 and 15 for a standard-rated line, so as far as validation goes, yes. Whether your supply attracts a rate other than 15 today is a tax question rather than an XML one, and it belongs to zatca.gov.sa.
You fixed this one — now avoid the next

ZATCA Tools builds the signature, the PIH, the counter and the encoding for you, and validates your data before it is sent. Start free, with no credit card.

Start for free

← All ZATCA error codes