The short answer
BR-23 fails when the line quantity has no unitCode attribute, as in <cbc:InvoicedQuantity>2</cbc:InvoicedQuantity>. Add unitCode with the UN/ECE Recommendation 20 code for the unit the quantity is counted in, and use the same code on cac:Price/cbc:BaseQuantity if the line has one.
The attribute has to sit on the quantity element itself. A unitCodeListID without unitCode, or a unit mentioned only in the item name or description, does not count.
What the rule checks
For each cac:InvoiceLine or cac:CreditNoteLine, the rule passes when the line quantity element carries an attribute named unitCode. It checks that the attribute exists, not what it says.
An empty or blank attribute gets past this rule and is caught by others. When tried, unitCode="" and unitCode=" " each reported BR-CL-23, because neither is a recognised code, and PEPPOL-EN16931-R130, because neither matches the C62 on the base quantity. BR-23 itself was not reported.
A line with no quantity element at all fails here too, together with BR-22, because there is no element to carry the attribute.
The unit on cac:Price/cbc:BaseQuantity plays no part in this rule. When the unit was removed from both the quantity and the base quantity, only BR-23 was reported: the Peppol unit comparison runs only on a base quantity that has a unit.
| Term | Meaning | UBL element |
|---|---|---|
| BT-130 | Invoiced quantity unit of measure code | cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode (cac:CreditNoteLine/cbc:CreditedQuantity/@unitCode in a credit note) |
| BT-129 | Invoiced quantity | cac:InvoiceLine/cbc:InvoicedQuantity |
| BT-150 | Item price base quantity unit of measure code | cac:InvoiceLine/cac:Price/cbc:BaseQuantity/@unitCode |
How an integration ends up here
Possible causes, from the shape of the rule rather than from measured usage:
- The source system stores quantities as plain numbers and has no unit field for the export to use.
- The serialiser writes attributes only when a value is present, and the unit is null for service lines or manually entered lines.
- The unit is sent in
unitCodeListID, or only in the item description, and theunitCodeattribute is left out. - A credit note mapper builds
cbc:CreditedQuantityfrom scratch and copies the number without the attribute.
How to fix it
- Find the line from the finding location, then find the unit of its quantity in the source: the sales unit of the product, or the unit the service is charged in.
- Map that unit to its UN/ECE Recommendation 20 code, for example
C62for one,H87for piece,HURfor hour orKGMfor kilogram, and write it in upper case tounitCodeoncbc:InvoicedQuantityorcbc:CreditedQuantity. - Write the same code on
cac:Price/cbc:BaseQuantitywhen the line has one.PEPPOL-EN16931-R130rejects a base quantity whose unit differs from the quantity unit. - Do not answer the Peppol finding by stripping the unit from the base quantity as well. That silences
PEPPOL-EN16931-R130and leavesBR-23exactly where it was.
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 quantity is 2 with no unit, while the price is per 1 C62
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity>2</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
<!-- item omitted from this fragment -->
<cac:Price>
<cbc:PriceAmount currencyID="GBP">12.5</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>Fragment of the corrected invoice: the quantity and the price base quantity are both in C62
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">2</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="GBP">25.00</cbc:LineExtensionAmount>
<!-- item omitted from this fragment -->
<cac:Price>
<cbc:PriceAmount currencyID="GBP">12.5</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>The only difference is unitCode="C62" on cbc:InvoicedQuantity, missing in the failing invoice and present in the corrected one. The failing document also reports PEPPOL-EN16931-R130 at cbc:BaseQuantity: the price base quantity says C62, and a quantity with no unit cannot match it. Adding the attribute to the quantity clears both.
What the validator reported
- The failing invoice reports BR-23 and PEPPOL-EN16931-R130. 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
cbc:InvoicedQuantityin a UBLInvoiceandcbc:CreditedQuantityin aCreditNote. When tried on a credit note, removing the attribute from the credited quantity reportedBR-23andPEPPOL-EN16931-R130, the same pair as the invoice. - The UBL schema treats
unitCodeas optional, so its absence passes the XSD layer and is left to this rule. - Which code is acceptable is decided by
BR-CL-23, not here. Once the attribute exists, a value such asEACHorpcsmoves the finding to that rule.
Related rules
- BR-CL-23 checks that the unit code, once present, is a recognised UN/ECE code
- PEPPOL-EN16931-R130 requires the price base quantity to use the same unit as the line quantity
- BR-22 reports a line with no quantity element at all, and fires with this rule
- BR-21 is the other presence check on the line itself, for the line identifier
- 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-23 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.
