Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Eseperio/verifactu-php/llms.txt
Use this file to discover all available pages before exploring further.
Verifactu PHP surfaces errors at several distinct layers: pre-flight model validation, business rejections returned by AEAT in a structured response, and lower-level SOAP or runtime exceptions. Understanding which layer an error comes from determines both how to catch it and how to recover.
1. Model Validation Errors
Every model (InvoiceSubmission, InvoiceCancellation, InvoiceQuery, …) exposes a validate() method. Call it before submitting to AEAT to catch field-level problems early.
Return value: An array — empty ([]) when the model is valid, or array<string, string[]> mapping each fully-qualified property key (e.g. ClassName::$property) to an array of error messages when invalid.
use eseperio\verifactu\models\InvoiceSubmission;
$invoice = new InvoiceSubmission();
// ... set properties ...
$result = $invoice->validate();
if (!empty($result)) {
// $result is an associative array: [ 'ClassName::$propertyName' => ['error message', ...], ... ]
foreach ($result as $property => $errors) {
echo "Property '{$property}' has errors: " . implode(', ', $errors) . "\n";
}
// Stop here — do not submit an invalid model
}
VerifactuService also calls validate() internally before building the SOAP request and will throw an InvalidArgumentException if !empty($validation), so checking manually gives you a cleaner error surface.
2. AEAT Response Errors
A successful SOAP call does not always mean AEAT accepted the invoice. The response is parsed into an InvoiceResponse object whose submissionStatus field reflects the overall outcome.
Checking the Submission Status
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceResponse;
$response = Verifactu::registerInvoice($invoice);
if ($response->submissionStatus !== InvoiceResponse::STATUS_OK) {
// STATUS_OK constant = 'Correcto'
// One or more invoices in the batch were rejected by AEAT
foreach ($response->lineResponses as $line) {
echo "Error code: " . ($line['CodigoErrorRegistro'] ?? 'N/A') . "\n";
echo "Error message: " . ($line['DescripcionErrorRegistro'] ?? 'N/A') . "\n";
echo "Error location: " . ($line['IDFactura'] ?? 'N/A') . "\n";
}
}
Using the getErrors() Helper
InvoiceResponse provides a convenience method that collects all non-empty error codes and their descriptions into a flat array:
$errors = $response->getErrors();
// Returns: [ 'errorCode' => 'description', ... ]
foreach ($errors as $code => $description) {
echo "AEAT error {$code}: {$description}\n";
}
Querying for Errors
For QueryResponse objects (returned by Verifactu::queryInvoices()), check the queryResult property instead:
use eseperio\verifactu\models\QueryResponse;
$result = Verifactu::queryInvoices($query);
if ($result->queryResult !== 'ConDatos') {
// No records returned or an error condition was indicated
echo "Query result: " . $result->queryResult . "\n";
}
3. SOAP Communication Errors
When the SOAP layer itself fails — network timeouts, TLS errors, malformed XML, or AEAT rejecting the SOAP envelope before even parsing the business payload — a SoapFault exception is thrown.
The AEAT SOAP gateway also returns numeric fault codes inside the fault message. The most common ones are:
| Code | Description |
|---|
| 100 | The SOAP request signature is not valid |
| 101 | The SOAP request is empty |
| 102 | The SOAP request is not well-formed: SOAP Envelope not found |
| 103 | The SOAP request is not well-formed: SOAP Body not found |
| 104 | The SOAP request is not well-formed: SOAP Header not found |
| 106 | The certificate used in the SOAP signature is on a blocklist or is a test certificate |
Code 106 appears when you present a certificate to the production endpoint that AEAT has revoked, flagged, or that is recognized only as a test/homologation certificate. Check that you are using ENVIRONMENT_SANDBOX during development.
4. Exception Types
The library throws standard PHP exception types with descriptive messages:
| Exception | When It Is Thrown |
|---|
InvalidArgumentException | Invalid configuration parameter, unsupported environment value, or model validation failure |
RuntimeException | File-system problems (certificate not readable), errors calling the AEAT service after a SoapFault |
SoapFault | Low-level SOAP transport error or protocol fault from AEAT |
Exception | Unexpected or uncategorized error |
5. Complete Try/Catch Example
Always wrap AEAT calls in a try/catch block that handles all exception types in order from most specific to most general:
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceResponse;
try {
// Validate the model before calling AEAT
$validation = $invoice->validate();
if (!empty($validation)) {
foreach ($validation as $property => $errors) {
echo "Validation error in '{$property}': " . implode(', ', $errors) . "\n";
}
return; // do not proceed
}
$response = Verifactu::registerInvoice($invoice);
if ($response->submissionStatus === InvoiceResponse::STATUS_OK) {
// Invoice accepted — store the CSV for your records and QR generation
echo "Accepted. AEAT CSV: " . $response->csv . "\n";
} else {
// AEAT returned a business rejection
foreach ($response->lineResponses as $line) {
echo "Rejected — code: " . ($line['CodigoErrorRegistro'] ?? '')
. " — " . ($line['DescripcionErrorRegistro'] ?? '') . "\n";
}
}
} catch (\InvalidArgumentException $e) {
// Bad input — certificate path wrong, unsupported environment, etc.
error_log('Configuration or input error: ' . $e->getMessage());
} catch (\RuntimeException $e) {
// File-system issue or AEAT service call failure
error_log('Runtime error: ' . $e->getMessage());
} catch (\SoapFault $e) {
// SOAP-level error — check the fault code in the message
error_log('SOAP fault: ' . $e->getMessage());
// faultcode and faultstring are also available on SoapFault directly
error_log('Fault code: ' . $e->faultcode);
} catch (\Exception $e) {
// Catch-all for any other unexpected errors
error_log('Unexpected error: ' . $e->getMessage());
}
6. The ErrorRegistry Dictionary
All AEAT error codes have official Spanish-language descriptions. The library ships the complete official mapping in src/dictionaries/ErrorRegistry.php. You can look up any code programmatically:
use eseperio\verifactu\dictionaries\ErrorRegistry;
// Look up by numeric code
$description = ErrorRegistry::getDescription(1108);
// => 'El NIF del IDEmisorFactura debe ser el mismo que el NIF del ObligadoEmision.'
// Alias method — identical behaviour
$description = ErrorRegistry::getErrorMessage(4104);
// => 'Error en la cabecera: el valor del campo NIF del bloque ObligadoEmision no está identificado.'
Error Code Ranges
| Range | Category |
|---|
1100 – 1284 | Individual invoice field errors — invoice is rejected |
2000 – 2008 | Warnings — invoice is accepted but must be corrected |
3000 – 3004 | Duplicate or permission errors |
3500 – 3503 | Database technical errors |
4102 – 4140 | Submission-level errors — entire batch is rejected |
7. NIF Census Mismatch Error
Error 4104 — ObligadoEmision NIF not identified in AEAT censusIf AEAT returns error code 4104 (“El valor del campo NIF del bloque ObligadoEmision no está identificado”), it means the issuerNif and/or issuerName you provided in the invoice do not exactly match the data held by the AEAT for that tax ID.To fix this:
- Log in to the AEAT website and navigate to “Mis datos censales”.
- Verify that the NIF and the exact legal name (NombreRazon) match character-for-character — including punctuation, abbreviations such as
S.L. vs SL, and accented characters.
- Update your integration to use the exact values returned by the AEAT census.
The same rule applies to TEST_ISSUER_NIF and TEST_ISSUER_NAME when running sandbox integration tests.
8. Best Practices
- Validate before submitting. Call
$model->validate() explicitly so you see field-level errors without making a network round-trip.
- Always use try/catch. SOAP calls are inherently I/O operations — network failures, TLS issues, and timeouts will occur in production.
- Log the raw SOAP payload on errors. When debugging a
SoapFault, the raw request XML ($client->__getLastRequest()) and response ($client->__getLastResponse()) are the most valuable diagnostic artefacts.
- Store the CSV. On
STATUS_OK, persist $response->csv immediately — it is the AEAT’s immutable reference for the invoice.
- Consult
ErrorRegistry for user-facing messages. AEAT error codes are terse; translate them via ErrorRegistry::getDescription($code) before displaying them to end-users or support staff.