The short answer
BR-45 fails for each cac:TaxSubtotal that has no cbc:TaxableAmount child. Add it as the first child of the subtotal, holding the net total for that category and rate: the line net amounts, plus document-level charges, minus document-level allowances, in the document currency.
Expect the arithmetic rules for the breakdown to fail beside it. They start from the taxable amount and cannot pass while it is missing.
What the rule checks
The rule runs on every cac:TaxSubtotal inside a cac:TaxTotal and passes when a cbc:TaxableAmount element is present. The finding location is the subtotal that lacks one.
UBL treats the element as optional, so a missing taxable amount passes the XSD layer and is first reported here. An empty cbc:TaxableAmount is a different case: it is not a valid amount, and when tried it failed the XSD layer, so this rule and the rest of the business rules never ran.
Any value satisfies this rule, zero included. When tried, a taxable amount of 0.00 on a 20% breakdown with 5.00 of tax cleared BR-45 and still reported BR-CO-17, BR-S-08 and BR-S-09.
The category makes no difference to this rule. An exempt breakdown without a taxable amount reported it together with BR-E-08, the exempt taxable amount check, when tried.
| Term | Meaning | UBL element |
|---|---|---|
| BG-23 | VAT breakdown | cac:TaxTotal/cac:TaxSubtotal |
| BT-116 | VAT category taxable amount | cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- The breakdown is built from a VAT summary that stores only rate and tax, and the net amount per rate is never carried across.
- The taxable amount is optional in the mapping and is dropped when null, for example on a breakdown created by a manual VAT adjustment.
- A zero taxable amount is treated as having no value and suppressed by the serialiser.
How to fix it
- For each
cac:TaxSubtotal, read its category and rate fromcac:TaxCategory. - Sum the
cbc:LineExtensionAmountof the lines in that category and rate, add the root-level charges and subtract the root-level allowances in the same category and rate, using decimal arithmetic. - Write the result as
cbc:TaxableAmount, with the document currency incurrencyID, as the first child of the subtotal and beforecbc:TaxAmount. - Check the subtotal
cbc:TaxAmountagainst the restored figure, sinceBR-CO-17and the category rule compare it with taxable amount x rate / 100.
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: the VAT breakdown has a tax amount but no taxable amount
<cac:TaxTotal>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<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>Fragment of the corrected invoice: the taxable amount of 25.00 opens the breakdown
<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>The corrected invoice has <cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount> at the start of the subtotal; nothing else differs. The failing document reports three more rules, all because the base figure is gone. BR-CO-17 and BR-S-09 multiply the taxable amount by the rate to check the 5.00 of tax, and without a taxable amount there is no product to compare. BR-S-08 compares the Standard rated taxable amount with the 25.00 line and finds no amount on the breakdown side. Restoring the element clears all four.
What the validator reported
- The failing invoice reports BR-45, BR-CO-17, BR-S-08 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
InvoiceandCreditNote. A credit note missing the taxable amount reported the same four rules when tried. - Reported by the EN 16931 layer; the Peppol layer passes the recorded example.
- Which arithmetic rules come along depends on the category of the breakdown:
BR-S-08andBR-S-09for Standard rated,BR-E-08for exempt, and the matching rules for the other categories. - The tax total in a VAT accounting currency has no subtotal, so it has no taxable amount and is not checked here.
Related rules
- BR-CO-17 checks each breakdown tax amount against the taxable amount and rate
- BR-S-08 checks the Standard rated taxable amount against the lines, allowances and charges
- BR-S-09 repeats the tax calculation for Standard rated breakdowns
- BR-E-08 checks the exempt taxable amount and comes with this rule on an exempt breakdown
- Browse every rule in the reference
- Background: Understanding EN 16931 validation errors
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-24.1: source checked 2026-09-24, explanation last updated 2026-09-24.
The official definition of BR-45 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.
