«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.
XML parsing (Parse)
XSD schema validation
Namespaces and UBL order
Business rules (BR)
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:
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:
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.
The rule: replace & with &, and< with <, and> with > 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.
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:
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:ProfileIDwhich specifies the processing type (reporting or clearance).cbc:UUIDthe invoice’s unique identifier.cac:AdditionalDocumentReferencewhich carries the QR code and the hash.cbc:InvoiceTypeCodewith the attributenamethat 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.
UBLExtensions (the stamp)
Identifiers and dates (cbc)
Seller and buyer (cac)
Tax and totals
Line items InvoiceLine
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:
cbcfor the CommonBasicComponents: simple values such as the identifier, the date, and the amount.cacfor the CommonAggregateComponents: composite blocks such as the supplier data and the invoice line items.extfor the CommonExtensionComponents: carries the digital signature insideUBLExtensions.
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.
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:
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:
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:
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.
| Word in the message | Layer |
|---|---|
| parse / not well-formed | XML parsing |
| cvc- / schema | XSD schema validation |
| not bound / namespace | Namespaces |
| BR- / rule | Business rules |
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.
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:
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
currencyIDwith a wrong hard-coded value in every file. Fix the template once, not every invoice. - Wrong date format: the schema enforces the
YYYY-MM-DDformat for the date. A template that writes the date in a local format such as24/06/2026fails all files. - Wrong decimal separator: using a comma instead of a dot in amounts. The schema accepts only the dot:
150.00not150,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:
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.
- 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.
- Classify the layer: Is the keyword parse, or namespace, or cvc, or BR? This determines the section you work in.
- Validate locally with xmllint: Run the file against the official XSD before any submission. It catches most errors without a network.
- Compare against a known valid file: Keep an invoice that succeeded previously as a reference, and compare the structure, not the values.
- 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.
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.
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-8in the first line, and the file is actually saved in this encoding without a BOM. - All prefixes
cbcandcacandextare 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
xmllintagainst 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.