The short answer
BR-CO-17 fails when the cbc:TaxAmount of a cac:TaxSubtotal does not agree with that subtotal's cbc:TaxableAmount multiplied by its cac:TaxCategory/cbc:Percent. The correct value is taxable amount x rate / 100, rounded to two decimals. Recalculate it, then carry the corrected figure into the VAT total and the totals that depend on it.
The rule runs on every VAT breakdown whatever its category. On a Standard rated breakdown the same arithmetic is checked again by BR-S-09, so the two are normally reported together.
What the rule checks
For a breakdown with a non-zero rate, the rule calculates taxable amount x rate / 100, rounds that to two decimals, and compares it with the tax amount. The comparison is not exact: it passes when the two differ by less than 1.00 and fails at a difference of exactly 1.00 or more. On 25.00 at 20%, where the calculated amount is 5.00, tax amounts of 4.01 and 5.99 pass and 4.00 and 6.00 fail.
Passing that tolerance does not make the figure right. A breakdown of 5.90 on 25.00 at 20% validates when the totals follow it, and the invoice still states 0.90 too much VAT. Treat the tolerance as slack in the checker, not as a rounding allowance you can use.
When the rate is 0, or the breakdown has no cbc:Percent at all, the tax amount only has to round to zero as a whole number: 0.40 gets past this rule and 0.50 does not. The category rules are stricter; a Zero rated breakdown with 0.40 of tax is still rejected by BR-Z-09.
Both amounts are compared as absolute values, so the rule does not look at the sign. A tax amount of -5.00 on a taxable amount of 25.00 passes it.
Only the three values inside the breakdown are read. Whether the document VAT total agrees with the breakdowns is a separate check, BR-CO-14.
| Term | Meaning | UBL element |
|---|---|---|
| BT-117 | VAT category tax amount | cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount |
| BT-116 | VAT category taxable amount | cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount |
| BT-119 | VAT category rate | cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- VAT is calculated per line and the line results are summed into the breakdown, so it drifts away from taxable amount x rate. Pennies of drift stay inside the tolerance; a larger gap usually means a line was taxed at a different rate from the one the breakdown declares.
- The tax amount comes from a ledger posting while the taxable amount and rate are rebuilt from the lines, and the two were produced at different times or from different data.
- The rate is written as a fraction,
0.2instead of20, so the calculated amount is a hundredth of what was intended. - The taxable amount was changed by a document-level allowance or charge and the tax amount was not recalculated.
- A gross amount, or the tax of a different category, is mapped into
cbc:TaxAmount.
How to fix it
- For each
cac:TaxSubtotal, takecbc:TaxableAmountandcac:TaxCategory/cbc:Percentas sent. - Calculate taxable amount x rate / 100 in decimal arithmetic and round the result to two decimals. Write it to the subtotal
cbc:TaxAmount. - If the calculated figure disagrees with what your ledger holds, find out which of the taxable amount, the rate or the posted tax is wrong before sending. Do not nudge the amount until it falls inside the tolerance.
- Set
cac:TaxTotal/cbc:TaxAmountto the sum of the breakdown tax amounts (BR-CO-14), then recalculatecbc:TaxInclusiveAmount(BR-CO-15) andcbc:PayableAmount(BR-CO-16).
The recorded example has one breakdown: 25.00 taxable at 20%.
Calculated tax: 25.00 x 20 / 100 = 5.00 Tax amount sent: 6.50 Difference: 1.50, not below 1.00, so the rule fails Corrected: tax amount 5.00, VAT total 5.00, total with VAT 25.00 + 5.00 = 30.00
Validate your corrected invoice
Before and after
These are fragments, not complete documents. The complete synthetic documents they come from are linked below.
Fragment of the failing invoice: 6.50 of VAT on 25.00 at 20%, with the totals built on the wrong figure
<cac:TaxTotal>
<cbc:TaxAmount currencyID="GBP">6.50</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">6.50</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>20</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="GBP">25.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="GBP">31.50</cbc:TaxInclusiveAmount>
<!-- allowance, charge, prepaid and rounding totals (all 0.00) omitted from this fragment -->
<cbc:PayableAmount currencyID="GBP">31.50</cbc:PayableAmount>
</cac:LegalMonetaryTotal>Fragment of the corrected invoice: the breakdown is 5.00 and the totals follow
<cac:TaxTotal>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>20</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="GBP">25.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="GBP">30.00</cbc:TaxInclusiveAmount>
<!-- allowance, charge, prepaid and rounding totals (all 0.00) omitted from this fragment -->
<cbc:PayableAmount currencyID="GBP">30.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>Four values differ: the breakdown cbc:TaxAmount and the document cac:TaxTotal/cbc:TaxAmount go from 6.50 to 5.00, and cbc:TaxInclusiveAmount and cbc:PayableAmount go from 31.50 to 30.00. The failing document also reports BR-S-09, because the breakdown is Standard rated and that rule repeats the calculation for the category. No totals rule fires, because the failing totals were built consistently on the wrong 6.50. Had only the breakdown been wrong, BR-CO-14 would be reported as well.
What the validator reported
- The failing invoice reports BR-CO-17 and BR-S-09. The corrected document passes every layer with no findings.Download the failing XMLDownload the corrected XML
Recorded on phive 12.1.0 / phive-rules-peppol 4.5.6 / Saxon-HE 12.10, the engine behind the free validator, using synthetic data. A recorded result is regression evidence for these documents; it is not a certification.
Where it applies
- Applies to UBL
InvoiceandCreditNotealike. A credit note with the same 6.50 on 25.00 at 20% reports the same two rules. - Every
cac:TaxSubtotalinside acac:TaxTotalis checked, in any VAT category. The category-specific counterparts, such asBR-S-09for Standard rated andBR-Z-09for Zero rated, run in addition. - Amounts are compared in the currency they are written in. The rule does not convert currencies.
- A breakdown that passes this rule can still be wrong in its taxable amount. For Standard rated breakdowns
BR-S-08checks that against the lines, allowances and charges.
Related rules
- BR-S-09 repeats this calculation for Standard rated breakdowns and fires alongside it
- BR-CO-14 checks that the document VAT total is the sum of the breakdown tax amounts
- BR-CO-15 checks the total with VAT once the VAT total has been corrected
- BR-S-08 checks the Standard rated taxable amount that this calculation starts from
- Browse every rule in the reference
Scope and source
Written for Peppol BIS Billing 3.0.21 (May 2026), EN 16931 1.3.16, as applied to UBL 2.1 Invoice and CreditNote documents. Other profiles, syntaxes and releases can define this identifier differently. Guidance version 2026-09-20.1: source checked 2026-09-20, explanation last updated 2026-09-20.
The official definition of BR-CO-17 carries the normative wording and test. This page is our explanation of it, not a copy.
Guidance does not change the engine verdict. Fixing this finding does not mean the document passes every layer, and validation does not certify legal or tax compliance or transmit a document over Peppol.
