Qoyod
Pricing

Knowledge Base

XML Errors in the E-Invoice: Causes and Fixes

«XML errors in the e-invoice» appear when the Zakat, Tax and Customs Authority (ZATCA) system rejects the invoice file before it reaches the business-rules stage. These are structural errors in how the file itself is built: a missing tag, a wrong order of UBL elements, an incorrect namespace, or a file that does not match the XSD schema. The reader here is a developer or technical lead building the integration with the Fatoora platform, who needs the exact error message they will see and the precise fix for each case.

This guide separates two kinds of errors that many people confuse. The first kind is structural: the file is technically broken. The second is a validation error at the business-rules level (such as the total tax not matching the sum of the line items). This article focuses on the structural level exclusively. Business-rules errors are handled in a separate guide, and you can refer to the invoice schema and its validation to understand how the system validates the file step by step. And to grasp the full picture of compliance requirements, see the Qoyod e-invoicing guide.

The difference between a structural error and a business-rules error

Before any diagnosis, identify the error layer. The structural layer happens first in the processing pipeline. If the file fails there, it never even reaches the business-rules check. Understanding this order saves you hours of searching in the wrong place.

A structural error means the parser cannot read the file, or the file does not match the approved XSD schema. Examples: a tag that was not closed, an element out of place within a UBL sequence, an undefined namespace prefix, or an unsupported character encoding. The system responds here with errors of the XML-parsing or schema-validation type.

A business-rules error is entirely different. Here the file is structurally sound and reads without a problem, but it violates a logical rule. Example: the total tax does not equal the rate multiplied by the base, or the issue date is in the future. These cases have their own error codes in the BR family, and are handled after the structural check passes successfully.

The practical rule: if you see a message with the word schema, parse, or namespace, you are in the structural layer. If you see a code starting with BR or a message about a mismatch in amounts, you are in the business-rules layer. This article covers the first. For the second, see the dedicated guide on rule-level validation errors.

Why this order matters to the developer

A developer who starts by fixing amounts while the real error is a missing tag wastes their time. The processing pipeline on the Fatoora platform checks structure first. A structurally broken file is rejected immediately with a parsing message, and no message about amounts will appear until the file becomes readable. Always start from the top layer.

The invoice processing pipeline and where XML fails
The layers the file passes through, and the XML errors at the start.
1

XML parsing (Parse)

2

XSD schema validation

3

Namespaces and UBL order

4

Business rules (BR)

Structural XML errors are caught in the first two layers, before business rules.

1. Malformed XML structure errors (Malformed XML)

These are the simplest and most common errors. The file does not match the basic rules of XML, so the parser fails before any other step. Recurring causes: a tag that was not closed, incorrect nesting between tags, a reserved character that was not escaped, or more than one root element.

The typical error message you will see from the parser looks like: Premature end of file or The element type "cbc:ID" must be terminated by the matching end-tag. This message always points to a specific location in the file, so read the line and column number it mentions.

In the following example, the tag cbc:ID was not closed, which is a direct cause of the incomplete-tag message:

<!-- Error: the cbc:ID tag was not closed -->
<cac:AccountingSupplierParty>
  <cac:Party>
    <cbc:ID>300012345600003
    <cbc:Name>Example Est.</cbc:Name>
  </cac:Party>
</cac:AccountingSupplierParty>

The fix is to close the tag explicitly. Every element that opens must be closed with a tag that matches it exactly in name and prefix:

<!-- Correct -->
<cac:AccountingSupplierParty>
  <cac:Party>
    <cbc:ID>300012345600003</cbc:ID>
    <cbc:Name>Example Est.</cbc:Name>
  </cac:Party>
</cac:AccountingSupplierParty>

Reserved characters inside text values

A hidden cause of a broken file is inserting a reserved character inside a text value without escaping it. The characters & and< and> have a special meaning in XML. A company name containing an English ampersand written as the raw & symbol breaks the file immediately.

<!-- Error: the & symbol is not escaped -->
<cbc:RegistrationName>Ahmad & Sons Co.</cbc:RegistrationName>

<!-- Correct: use the &amp; entity -->
<cbc:RegistrationName>Ahmad &amp; Sons Co.</cbc:RegistrationName>

The rule: replace & with &amp;, and< with &lt;, and> with &gt; inside any text content. If the value contains many symbols, use a CDATA section instead of manual escaping. Note that the Fatoora platform does not accept CDATA in every field, so limit it to free-text fields.

Duplicate root element

A valid XML file has exactly one root element. Some generators write the <Invoice> tag twice by mistake when batching, producing a file with two roots. The error message is of the type The markup in the document following the root element must be well-formed. The fix is to generate one file per invoice, or wrap the batch in a single envelope if the interface supports bulk submission.

2. Missing UBL elements or in the wrong order

E-invoicing in Saudi Arabia follows the UBL 2.1 standard. This standard does not only define the required elements; it also enforces their order within each sequence. An element in the wrong position fails schema validation even if it is present and holds a correct value.

The common error: placing cbc:IssueDate after cbc:InvoiceTypeCode while the schema enforces the opposite. The typical error message: Invalid content was found starting with element 'cbc:IssueDate'. One of '{...}' is expected. This message tells you exactly which element the system expected in that position.

<!-- Error: IssueDate came before ID, and the standard order starts with ID -->
<Invoice>
  <cbc:ProfileID>reporting:1.0</cbc:ProfileID>
  <cbc:IssueDate>2026-06-24</cbc:IssueDate>
  <cbc:ID>INV-001</cbc:ID>
</Invoice>

The fix is to return the elements to the order the schema enforces. In UBL, cbc:ProfileID comes then cbc:ID comes then cbc:UUID comes then cbc:IssueDate:

<!-- Correct: the order matches the schema sequence -->
<Invoice>
  <cbc:ProfileID>reporting:1.0</cbc:ProfileID>
  <cbc:ID>INV-001</cbc:ID>
  <cbc:UUID>3cf5ee18-ee25-4ea1-bf7b-9b2c4d4f...</cbc:UUID>
  <cbc:IssueDate>2026-06-24</cbc:IssueDate>
</Invoice>

Missing mandatory elements

Some elements are mandatory in every invoice. Their absence triggers a message of the type cvc-complex-type.2.4.b: The content of element 'Invoice' is not complete. One of '{...}' is expected. The most frequently forgotten elements:

  • cbc:ProfileID which specifies the processing type (reporting or clearance).
  • cbc:UUID the invoice’s unique identifier.
  • cac:AdditionalDocumentReference which carries the QR code and the hash.
  • cbc:InvoiceTypeCode with the attribute name that describes the invoice type.

Check the complete list of mandatory elements before generating any file. The Fatoora platform responds with a list of the missing elements, but it sometimes stops at the first structural error, so you may not see the full list at once.

The mandatory order of UBL elements
The order in which elements must appear, otherwise validation fails.
Element order

UBLExtensions (the stamp)

Identifiers and dates (cbc)

Seller and buyer (cac)

Tax and totals

Line items InvoiceLine

A wrong element order produces a schema-validation error.

3. Namespace errors (Namespaces: cac / cbc / ext)

Namespaces are the single biggest source of errors that confuse developers new to UBL. Every prefix in the invoice belongs to a specific namespace that must be declared precisely in the root element. Any difference in the URI, or an undefined prefix, fails the file.

The three core prefixes in a Saudi UBL invoice:

  • cbc for the CommonBasicComponents: simple values such as the identifier, the date, and the amount.
  • cac for the CommonAggregateComponents: composite blocks such as the supplier data and the invoice line items.
  • ext for the CommonExtensionComponents: carries the digital signature inside UBLExtensions.

The most common error is using a prefix without declaring it in the root. The error message: The prefix "cbc" for element "cbc:ID" is not bound. This means the parser found a prefix with no definition.

<!-- Error: cbc and cac were not declared in the root -->
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
  <cbc:ID>INV-001</cbc:ID>
  <cac:AccountingSupplierParty>...</cac:AccountingSupplierParty>
</Invoice>

The fix is to declare every prefix in the root element with the exact official URIs. Copy the URIs literally, since one different character invalidates the declaration:

<!-- Correct: all prefixes declared in the root -->
<Invoice
  xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
  xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
  xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
  xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
  <cbc:ID>INV-001</cbc:ID>
</Invoice>

The wrong default-namespace error

Another subtle error: defining the default namespace with an incorrect URI. If you write xmlns with the Invoice-1 URI instead of Invoice-2, the parser will read the elements in the wrong namespace, so schema validation fails without a clear message about the cause. Check the version number in every URI: the basic components end with -2 in UBL 2.1.

The difference between not declaring and declaring incorrectly

Distinguish between two cases. The first is a prefix that is not defined at all, and its message is prefix is not bound. The second is a prefix that is defined but with a wrong URI, and its message comes from schema validation because the parser cannot find the element in the expected namespace. You fix the first by adding the declaration, and the second by correcting the URI.

4. Schema validation failure (XSD Validation)

After the parser reads the file successfully, XSD schema validation follows. This layer verifies that every element is of the correct type, within the allowed limits, and in the enforced order. The file may be well-formed yet fail here.

The typical errors in this layer:

  • Wrong data type: placing text in a numeric field. Message: cvc-datatype-valid.1.2.1: '...' is not a valid value for 'decimal'.
  • A value outside a closed list: an unsupported invoice-type code. Message: cvc-enumeration-valid: Value '...' is not facet-valid with respect to enumeration.
  • An extra element not defined in the schema: adding a tag the standard does not recognize. Message: cvc-complex-type.2.4.a: Invalid content was found.

The following example places a text value in the amount field, which is a decimal-type field:

<!-- Error: a text value in a numeric field -->
<cbc:PayableAmount currencyID="SAR">one hundred fifty</cbc:PayableAmount>

<!-- Correct: a decimal number with a dot decimal separator -->
<cbc:PayableAmount currencyID="SAR">150.00</cbc:PayableAmount>

Note the mandatory currencyID attribute on every amount field. Its absence is a separate validation error, since amount fields in UBL require the currency to be stated explicitly. Use SAR for the Saudi riyal.

Closed-list values (Enumerations)

Several fields in the invoice accept values from a closed list only. The most prominent is the invoice-type code InvoiceTypeCode and the tax-category code. Placing a value outside the list returns an enumeration error. Check the approved list in the Fatoora platform documentation, and do not invent codes.

An example of a correct tax-category code: S for the standard-rated, Z for the zero-rated, E for the exempt. Any other letter fails validation.

The local validation tool before submission

Do not send the file to the Fatoora platform to discover a schema error. Validate locally first. Run your file against the official XSD files using a parser that supports validation, such as xmllint:

xmllint --schema UBL-Invoice-2.1.xsd invoice.xml --noout

This command prints all schema errors at once before any network connection. Make it part of your build pipeline, since it catches most structural errors early. A high number of errors in the schema layer usually means a wrong generation template, not scattered individual errors.

Read the error keyword to know its layer
Every keyword in the message points to a specific layer.
Word in the message Layer
parse / not well-formed XML parsing
cvc- / schema XSD schema validation
not bound / namespace Namespaces
BR- / rule Business rules
Classifying the keyword guides you to the place of the fix quickly.

5. Encoding errors and Arabic-character problems

Encoding is a hidden cause of many errors that look mysterious. The Saudi invoice contains Arabic text, and the wrong encoding turns the Arabic characters into garbled symbols or breaks the file at parse time.

The critical rule: always use UTF-8 , and declare it explicitly in the first line. Any other encoding such as Windows-1256 causes a Invalid byte 1 of 1-byte UTF-8 sequence message when an Arabic character is present.

<!-- Error: a wrong encoding declaration with Arabic content -->
<?xml version="1.0" encoding="Windows-1256"?>

<!-- Correct: UTF-8 declared explicitly -->
<?xml version="1.0" encoding="UTF-8"?>

The declaration alone is not enough. Make sure the file is actually saved in UTF-8 on disk. A mismatch between the declaration and the actual byte encoding is a common cause of garbled symbols. In your development environment, set the text editor, the database, and the output layer all to UTF-8.

Byte Order Mark (BOM)

Another subtle problem is the Byte Order Mark that some editors add at the start of a UTF-8 file. This mark is invisible bytes before the opening XML declaration, and some parsers may reject it with a Content is not allowed in prologmessage. The fix is to save the file in UTF-8 without a BOM.

Arabic characters in mandatory fields

Make sure the Arabic trade name of the supplier and buyer is written as proper Arabic text inside the text field, not transliterated in Latin letters. The Fatoora platform accepts Arabic text as long as the UTF-8 encoding is correct. The garbling you see in the extracted text is a symptom of wrong encoding, not an error in the original text.

6. Extension-block and signature errors (ext / UBLExtensions)

In the second phase, the invoice carries a digital signature inside the extension block ext:UBLExtensions. This block is one of the most complex places for structural errors, because it combines a strict order with a deeply nested structure. A single error in its position or content fails the entire file.

The first rule: the ext:UBLExtensions block must come at the very start of the invoice, before any cbc or cac element. Placing it after cbc:ID for example returns an ordering error from the schema. The correct position is immediately after the root tag:

<!-- Correct: the extension block is the first element inside the invoice -->
<Invoice xmlns="..." xmlns:cac="..." xmlns:cbc="..." xmlns:ext="...">
  <ext:UBLExtensions>
    <ext:UBLExtension>
      <ext:ExtensionContent>
        <!-- The digital signature goes here -->
      </ext:ExtensionContent>
    </ext:UBLExtension>
  </ext:UBLExtensions>
  <cbc:ProfileID>reporting:1.0</cbc:ProfileID>
  <cbc:ID>INV-001</cbc:ID>
</Invoice>

The second common error is modifying the invoice content after generating the signature. The signature computes a hash over the invoice structure. Any later change, even a whitespace or a reordered attribute, breaks the match, so the signature is rejected. The rule: generate the signature as the last step, and never touch the file afterwards.

Hash mismatch due to canonicalization

A hidden cause of a rejected signature is a difference in how XML is canonicalized between generation and verification. The Authority’s system computes the hash over a canonicalized form of the file. If your generator canonicalizes the file differently from the required standard, the hash differs and the file is rejected even though the signature is technically valid. Use the canonicalization algorithm specified in the Fatoora platform specification literally.

The error message here is often vague, pointing to a hash mismatch without explaining the cause. When you face it, first check that you did not modify the file after signing, then verify the canonicalization algorithm and the hash encoding (Base64) before anything else.

7. Recurring generation-template errors

When a single file fails, the error is usually in the invoice itself. When every invoice fails the same way, the error is in the generation template. Recognizing the pattern spares you from fixing symptoms instead of the cause.

The most common template errors:

  • A fixed attribute with a wrong value: a template that writes currencyID with a wrong hard-coded value in every file. Fix the template once, not every invoice.
  • Wrong date format: the schema enforces the YYYY-MM-DD format for the date. A template that writes the date in a local format such as 24/06/2026 fails all files.
  • Wrong decimal separator: using a comma instead of a dot in amounts. The schema accepts only the dot: 150.00 not 150,00.
  • Leading spaces inside values: a template that adds spaces or new lines inside a sensitive text value, so it fails at validation or breaks the hash.

The following example illustrates a date-format error, one of the most widespread template errors in our local environment:

<!-- Error: a local date format the schema does not accept -->
<cbc:IssueDate>24/06/2026</cbc:IssueDate>

<!-- Correct: the ISO format UBL enforces -->
<cbc:IssueDate>2026-06-24</cbc:IssueDate>

The issue time has a separate field cbc:IssueTime in the HH:MM:SSformat. Do not merge the date and time into one field, since each has its own element and type.

Testing the template on a batch before launch

Before launching any integration, generate a batch of ten varied invoices and run them through xmllint and the simulation together. A simple invoice may pass while an invoice with a discount, an exemption, or a foreign currency fails. Variety in testing exposes the template errors that do not show in the simplest case.

A practical scenario: tracing a rejection message end to end

Let us apply the methodology to a real case. You received a rejection message from the Fatoora platform reading: cvc-complex-type.2.4.a: Invalid content was found starting with element 'cbc:TaxAmount'. How do you diagnose it?

First, classify the layer. The prefix cvc- means you are in the schema-validation layer, not structure and not namespaces. The file reads successfully but violates the schema structure.

Second, read the mentioned element: cbc:TaxAmount. The message says it appeared in a position the schema does not expect. This points you directly to the element order in the tax block.

Third, compare against a valid file. In the successful file, cbc:TaxAmount comes inside cac:TaxTotal in a specific order. In your file, you discovered it was placed before a mandatory element that must precede it. The fix is to reorder the elements inside the block.

Fourth, validate locally with xmllint before resubmitting. The tool confirms the file now matches the schema without waiting for a network response. Five minutes of structured diagnosis saved repeated submission rounds.

This scenario sums up the article’s entire philosophy: the message tells you the layer, the layer determines the section, and the comparison against a valid file reveals the difference. No guessing, just a path.

How Qoyod helps you avoid XML errors

Qoyod, a cloud accounting software, generates the entire e-invoice file on your behalf, so you neither write XML manually nor deal with namespaces or the order of UBL elements. This eliminates the whole class of structural errors at the root. Here is what Qoyod offers specifically in this area:

  • Automatic compliant UBL file generation: Qoyod builds the file with the correct element order, declared prefixes, and UTF-8 encoding, without any intervention from you.
  • Compliance with both the first and second phases: Qoyod meets the Zakat, Tax and Customs Authority requirements in both e-invoicing phases.
  • Automatic exchange with the Authority: Qoyod sends invoices to the Fatoora platform directly, with no need for manual submission or file uploads.
  • A simulation environment for testing: It provides a simulation environment that lets you try the integration and verify acceptance before going live, without any risk to your real data.
  • Reducing human error: Automating generation removes the manual-copy and ordering errors that cause most structural rejection messages.

If you are building a custom integration, you can also make use of the API for a direct connection with your systems, while leaving the compliant file generation to Qoyod.

A structured diagnosis methodology for any XML error

When you face a rejection message, follow this sequence instead of guessing. Each step rules out an entire layer of errors, so you reach the cause faster.

  1. Read the full text of the message: The error message often states the element name and the line number. Do not ignore it and start guessing.
  2. Classify the layer: Is the keyword parse, or namespace, or cvc, or BR? This determines the section you work in.
  3. Validate locally with xmllint: Run the file against the official XSD before any submission. It catches most errors without a network.
  4. Compare against a known valid file: Keep an invoice that succeeded previously as a reference, and compare the structure, not the values.
  5. Check the encoding last: If the message is about bytes or the prolog, the problem is in the encoding or the BOM, not in the content.

This order mirrors the processing pipeline itself: from structure to namespaces to schema to encoding. Following it turns diagnosis from a random search into a defined path.

Start today

Let Qoyod generate the compliant XML file for you

Stop chasing missing tags and namespaces. Qoyod builds a UBL invoice compliant with the Authority automatically, and sends it to the Fatoora platform directly, through a safe simulation environment for testing.

Start your free trial

A quick checklist before any submission

Before you send any invoice to the Fatoora platform, run your file through this structural checklist. Each item rules out a common cause of rejection, and greatly shortens the correction cycle in production.

  • The encoding is declared UTF-8 in the first line, and the file is actually saved in this encoding without a BOM.
  • All prefixes cbc andcac andext are declared in the root element with the exact official URIs.
  • Every open tag has a matching closing tag, and every reserved character is escaped inside text values.
  • The elements are ordered according to the schema sequence, and all mandatory elements are present.
  • Amount fields carry the currencyIDattribute, the numbers use a dot decimal separator, and the dates are in ISO format.
  • The file passed xmllint against the official XSD locally before submission.
  • The signature was generated as the last step, and the file was not modified afterwards.

This checklist covers the structural layer completely. Passing it means any later rejection will be at the business-rules level, not the structural level, so you immediately know where to look.

Frequently asked questions

What is the difference between an XML error and a validation error in the e-invoice?

A structural XML error means the file itself is broken: a missing tag, an undefined namespace, or it does not match the XSD schema. A business-rules validation error means the file is structurally sound but violates a logical rule such as a mismatch in amounts. The first is rejected before the second in the processing pipeline. This article covers the structural, and to understand the validation layer see the invoice schema and its validation.

What does the message prefix is not bound mean?

It means you used a namespace prefix such as cbc or cac without declaring it in the root element. The fix is to add the xmlns:cbc andxmlns:cac andxmlns:ext declaration in the root tag with the exact official URIs.

Why do Arabic characters appear garbled in the invoice?

The cause is almost always a wrong encoding. Make sure the file is actually saved in UTF-8, that the declaration in the first line is encoding="UTF-8", and that there is no Byte Order Mark (BOM) at the start of the file.

Should I validate the file locally before sending it to the Authority?

Yes, and it saves a lot of time. Run your file against the official XSD files with a tool such as xmllint before any network connection. The tool catches most structural errors at once, without waiting for a response from the Fatoora platform.

What is the correct order of the cac, cbc, and ext prefixes in UBL?

cbc is for simple values such as the identifier, the date, and the amount; cac is for composite blocks such as the supplier data and the line items; and ext is for the extensions that carry the digital signature. The order within each sequence is enforced by the schema, and an element out of place fails validation even if its value is correct. See the XML invoice and its technical format to learn the full file structure.

Do I need to write XML manually to comply with e-invoicing?

No. A compliant accounting software like Qoyod generates the UBL file automatically with the correct order, encoding, and prefixes, and sends it to the Fatoora platform directly. This eliminates the whole class of structural XML errors, and lets you focus on your business instead of fixing tags.

Guides

Continue your learning journey

Explore the rest of Qoyod’s guides, or start applying what you’ve learned.

Live webinars hosted by the Qoyod team to help you use the software easily and answer your questions.

Discover Qoyod’s latest updates, ongoing improvements, and new features in one place.

Our team is ready to help you and provide instant support for any issue you face, around the clock.