The short answer
BR-CL-17 fails for each cac:TaxCategory/cbc:ID whose value is not a permitted VAT category code. That element appears in the VAT breakdown and on document-level allowances and charges, and the finding points at the offending cbc:ID. Replace the value with the matching code: in the recorded example the breakdown says SR where Standard rated is S.
Translate every internal tax code to one of the ten permitted codes before export. The codes on lines are checked separately, by BR-CL-18.
What the rule checks
The rule runs on every cbc:ID that is a direct child of a cac:TaxCategory, wherever that tax category sits. It does not read cac:ClassifiedTaxCategory on lines.
The accepted values are AE, L, M, E, S, Z, G, O, K and B. Surrounding spaces are ignored, but a space inside the value, the wrong letter case or any other code fails: when tried, s, A and 20 were all rejected.
An empty cbc:ID fails this rule as well, and when tried it also brought PEPPOL-EN16931-R008 for the empty element.
Membership of the list is all that is checked. Whether the category fits the items is left to the category rules, such as BR-S-01 and BR-S-08 for Standard rated.
| Term | Meaning | UBL element |
|---|---|---|
| BT-118 | VAT category code | cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:ID |
| BT-95 | Document level allowance VAT category code | cac:AllowanceCharge[cbc:ChargeIndicator = false]/cac:TaxCategory/cbc:ID |
| BT-102 | Document level charge VAT category code | cac:AllowanceCharge[cbc:ChargeIndicator = true]/cac:TaxCategory/cbc:ID |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- An accounting system tax code such as
SR,T1orSTDis written straight into the XML instead of being translated. - The code is lower-cased somewhere in the pipeline.
- A code is taken from the full UNCL5305 list, which has codes this rule does not accept, such as
A. - A rate, such as
20, is written in the category code field.
How to fix it
- Use the finding location to see which tax category holds the bad value: a breakdown under
cac:TaxTotal/cac:TaxSubtotal, or a root-levelcac:AllowanceCharge. - Map the internal code to its VAT category:
SStandard rated,ZZero rated,Eexempt,AEreverse charge,Kintra-community supply,Gexport outside the EU,Onot subject to VAT,LandMfor the Canary Islands and Ceuta and Melilla taxes,Bfor Italian split payment. - Use the same code on the lines, allowances, charges and breakdown for the same supply, so the breakdown still matches the items it totals.
- Keep the mapping in one table and fail the export on any code it does not contain.
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 line is coded S, the breakdown SR
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="GBP">25.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="GBP">5.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>SR</cbc:ID>
<cbc:Percent>20</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:Item>
<cbc:Name>Example service</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>20</cbc:Percent>
<!-- tax scheme omitted from this fragment -->
</cac:ClassifiedTaxCategory>
</cac:Item>Fragment of the corrected invoice: the breakdown uses the Standard rated code S
<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>The failing invoice writes SR in the breakdown cac:TaxCategory/cbc:ID and the corrected one writes S; that is the only difference. The failing document also reports BR-S-01: its line is still Standard rated, and a breakdown coded SR does not count as a Standard rated breakdown. Correcting the code clears both.
What the validator reported
- The failing invoice reports BR-CL-17 and BR-S-01. 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 withSRin its breakdown reported the same two rules when tried. - On a document-level allowance the effect reaches the amounts. When tried, an allowance coded
SRwas reported by this rule and byBR-S-08, because the allowance dropped out of the Standard rated taxable amount. - The same wrong code on both the line and the breakdown reported only
BR-CL-17andBR-CL-18when tried. Line and breakdown agreed with each other, so no category rule fired, but the document is still rejected. - Reported by the EN 16931 layer; the Peppol layer passes the recorded example.
Related rules
- BR-CL-18 applies the same code list to the VAT category on each line
- BR-S-01 is reported with this rule when a Standard rated breakdown is miscoded
- BR-47 requires the breakdown category code that this rule checks
- BR-S-08 fires when a miscoded allowance or charge drops out of the Standard rated taxable amount
- 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-CL-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.
