# PEPPOL-EN16931-R008: Remove empty elements from the document

An element has no text and no child elements. Peppol does not allow empty elements: leave out any optional element you have no value for.

- Layer: Peppol BIS Billing
- Severity: fatal (the document is invalid)
- Topics: Core fields, XML input
- Official definition: https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-peppol/PEPPOL-EN16931-R008/
- Explanation last updated: 2026-09-20

## The short answer

`PEPPOL-EN16931-R008` fails once for every element that has no child elements and no text other than whitespace. The finding location is the path of the empty element. If the value is optional and you do not have it, remove the element; if it is required, supply the value.

In the recorded example the optional buyer accounting reference, `cbc:AccountingCost`, is written as an opening and closing tag with nothing between them. An optional element with no value should simply be absent.

## What the rule checks

The rule has no condition of its own. It selects every element, at any depth and with any name, that has no child elements and whose text is empty after whitespace is stripped, and it reports each one.

All of these were reported when tried: an open and close tag with nothing between them, a self-closing tag, an element holding only spaces, tabs or line breaks, and an element holding only an XML comment.

Attributes do not count as content. An element with an attribute and no text is still empty.

Aggregates count too. A `cac:PaymentTerms` with no children is reported at that element. A parent whose children are all empty is not itself reported, but each empty child is, so a `cac:Contact` with three blank fields produces three findings.

A non-breaking space (`&#160;`) is not treated as whitespace, so an element containing only that character is not reported. That is a value made of one invisible character, not a fix.

| Term | Meaning | UBL element |
|---|---|---|
| - | Every element in the document, at any depth | `//*` |
| BT-19 | Buyer accounting reference (the element in the recorded example) | `cbc:AccountingCost` |

## How an integration ends up here

Possible causes, from the shape of the rule rather than from measured usage:

- The serialiser writes every mapped field and turns null or empty strings into empty elements instead of skipping them.
- A template has fixed optional elements such as `cbc:Note`, `cbc:AccountingCost` or the contact fields, and they are left blank when the source has nothing.
- Values are trimmed after the decision to emit the element, so a field holding only spaces becomes an empty element.
- A wrapper such as `cac:Contact` or `cac:PaymentTerms` is always emitted and its children are conditional, leaving an empty shell.

## How to fix it

1. Use the location in each finding to identify the element. There is one finding per empty element, so the list is complete.
2. For an optional element with no value, do not emit it. Make the serialiser skip null, empty and whitespace-only values as a general rule, not field by field.
3. After removing empty children, check the parent. If it now has no children, remove it too, or it becomes the next empty element reported.
4. For an element that another rule requires, fix the source data so there is a real value. Do not fill it with a placeholder such as a dash or `N/A` unless that is what the business data says.

## 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 buyer accounting reference is present but empty

```xml
<cbc:DocumentCurrencyCode>GBP</cbc:DocumentCurrencyCode>
<cbc:AccountingCost></cbc:AccountingCost>
<cbc:BuyerReference>BUYER-REF-001</cbc:BuyerReference>
```

Fragment of the corrected invoice: the element carries a value. Leaving the element out altogether also passes

```xml
<cbc:DocumentCurrencyCode>GBP</cbc:DocumentCurrencyCode>
<cbc:AccountingCost>COST-123</cbc:AccountingCost>
<cbc:BuyerReference>BUYER-REF-001</cbc:BuyerReference>
```

Only `cbc:AccountingCost` differs: empty in the failing document, `COST-123` in the corrected one. The failing document reports only `PEPPOL-EN16931-R008`. The UBL schema accepts an empty text element, and EN 16931 has no rule about this optional field, so the XSD and EN 16931 layers both pass.

### What the validator reported

- The failing invoice reports **PEPPOL-EN16931-R008**. The corrected document passes every layer with no findings.
  - [Download the failing XML](https://ironfang.uk/finance/rule-examples/PEPPOL-EN16931-R008-invalid.xml)
  - [Download the corrected XML](https://ironfang.uk/finance/rule-examples/invoice-rich.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 `Invoice` and `CreditNote` alike, and to every element in the document, including ones inside `cac:InvoiceLine` or `cac:CreditNoteLine`.
- Some empty elements do not get this far. An empty amount or an empty date, for example, fails the XSD layer first; the EN 16931 and Peppol layers are then skipped, so this rule is not reported for them.
- When a required element is empty, this rule arrives together with the rule that requires the value. An empty `cbc:CustomizationID`, for example, reports `BR-01` and `PEPPOL-EN16931-R004` as well.
- An empty element is no way round a presence rule. An empty `cbc:BuyerReference` is enough for `PEPPOL-EN16931-R003`, which only looks for the element, but it is reported here.

## Related rules

- [PEPPOL-EN16931-R003 requires a buyer reference or order reference, which must not be supplied as an empty element](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R003.md)
- [BR-01 is reported alongside this rule when the specification identifier is empty](https://ironfang.uk/docs/finance/rules/BR-01.md)
- [PEPPOL-EN16931-R004 is also reported when the specification identifier is empty](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R004.md)
- [FW-XML-001 covers the different case where the XML itself cannot be parsed](https://ironfang.uk/docs/finance/rules/FW-XML-001.md)

## 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-20.

[The official definition of PEPPOL-EN16931-R008](https://docs.peppol.eu/poacc/billing/3.0/rules/ubl-peppol/PEPPOL-EN16931-R008/) 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.

## Links

- [This rule as a web page](https://ironfang.uk/docs/finance/rules/PEPPOL-EN16931-R008)
- [Free Peppol invoice validator](https://ironfang.uk/tools/peppol-validator)
- [Rule index](https://ironfang.uk/docs/finance/rules.md)
- [Ironfang Finance API docs](https://ironfang.uk/docs/finance)
- The same rule is available to MCP clients as the tool `finance.rule.get` on https://mcp.ironfang.uk/mcp
