The invoice validates cleanly on your machine. Schema green, Schematron green, zero fired assertions. You send it, and the tax authority or the receiving access point rejects it. This is one of the most common support tickets in e-invoicing, and in almost every case the cause is one of six things — in a fairly predictable order.
First, the validation stack is not one thing
Before debugging, it helps to see how many independent gates a document passes through. Local validation typically covers the first two or three. The rejection almost always comes from one of the later ones.
1. You are running a different ruleset version
The most common cause by a wide margin. Rules, code lists and syntax bindings are released on separate cadences, and validation artefacts get copied into projects and then quietly age.
Three versions have to line up: the rules (Schematron), the code lists they reference, and the customization identifier the document declares. A document that declares one CIUS version and is validated against another will pass locally and fail at the far end, because the far end honours the declaration.
Make this checkableLog the version of every validation artefact alongside each validation result. If you cannot answer "which Schematron and which code list version produced this green result" in one query, you will spend days on this class of bug repeatedly.
2. Code list drift
Schematron rules frequently delegate value checks to external code lists — currencies, country codes, unit codes, tax categories, scheme identifiers, electronic address schemes. Those lists are versioned independently and updated more often than the rules that use them.
The failure is asymmetric and confusing: your older list may still contain a value that has since been withdrawn, so you accept a document the receiver rejects. Or your list lacks a newly added value, so you reject a document that is perfectly valid. Both look like "the rules are wrong".
3. The rejection is not a validation error at all
Worth checking early, because it saves a lot of wasted effort. Several rejection classes have nothing to do with the invoice content:
- Routing and addressing — the participant identifier is not registered, the receiver does not support that document type or that customization, or the capability lookup returned nothing.
- Transport-level — certificate expiry or trust chain issues, envelope metadata that disagrees with the payload, message size limits.
- Duplicate detection — the invoice number has been seen before for that supplier, often after a retry that partially succeeded.
- Authentication and entitlement — the credentials are valid but the account is not authorised for that taxpayer or that transaction type.
The tell is that the error text references identifiers, endpoints or credentials rather than business terms.
4. The receiving side runs rules you do not have
A tax authority or a large buyer commonly validates more than the published Schematron does. These extra checks are real, enforced, and frequently not published as executable artefacts:
- Registry checks — the seller's tax identifier must exist, be active on the invoice date, and match the registered legal name.
- Cross-document checks — a credit note must reference an invoice the authority has already cleared; a corrective document must reference a document in a correctable state.
- Sequence and timing — invoice numbering continuity, or submission within a window measured from the issue date.
- Buyer-side business rules — a valid purchase order reference, a known cost centre, a delivery note that matches. These live in an ERP, not in a specification.
Schematron proves the document is well-formed against a published model. It cannot prove the facts in it are true.
5. The rule fired but you never saw it
A category of self-inflicted wound worth checking honestly. Common shapes:
- Assertions with
flag="warning"filtered out of the report, when the receiver treats the same assertion as fatal. - A validation stage that fails open — an exception in the pipeline logged and swallowed, returning "no errors found" for "no validation performed".
- Namespace mismatches, where XPaths silently select nothing and every assertion becomes vacuously true. A rule that cannot find its context never fires and never complains.
- Validating a transformed or normalised copy of the document rather than the exact bytes that get sent.
The third one deserves particular suspicion. A validation run that reports zero fired assertions — not zero failures, zero fired — is usually a wiring bug, not a clean invoice.
6. Environment differences
Last, the boring one. Test platforms lag production, use different registry data, and sometimes relax checks that production enforces. A document that clears a sandbox may fail production because the sandbox's taxpayer registry is synthetic, or because production has a newer specification deployed.
The order to check in
When a rejection lands, this sequence resolves the majority of cases quickly:
- Read the error's origin. Which gate produced it — transport, network, authority, or business rule? That alone eliminates most of the list above.
- Compare declared versus validated ruleset. Take the customization identifier from the document and confirm your validator used exactly that version.
- Re-validate the sent bytes, retrieved from your archive rather than regenerated, against the current published artefacts.
- Check fired-assertion counts, not just failures. Zero fired is a red flag.
- Only then look at the mapping. If the ruleset and the artefacts are aligned and the assertions genuinely ran, the defect is in what you put in the field.
The structural fix is the same as the diagnostic one: pin and record the version of every validation artefact, validate the exact payload you transmit, and keep the receiving side's error responses where an engineer can actually read them. Most of this class of bug stops being mysterious the moment those three things are true.
- Identify the gate first. Transport, network, authority and business rules fail in visibly different ways.
- Version drift is the top cause — rules, code lists and the declared customization identifier must agree.
- Zero fired assertions is a wiring bug, not a clean bill of health.
- Validate the bytes you actually send, retrieved from the archive, not a regenerated copy.
