odoo/odoo · error · ValidationError

To explicitly indicate no (valid) VAT, use '/' instead.

Error message

To explicitly indicate no (valid) VAT, use '/' instead. 

What it means

Raised by base_vat's _run_vat_checks() when the partner's vat value is exactly one character long and is neither '/' nor handled by a softer validation mode. Odoo uses '/' as the explicit sentinel meaning 'this partner has no VAT number', so any other single-character VAT is treated as invalid input rather than being checked.

Source

Thrown at addons/base_vat/models/res_partner.py:117

    )
    # Field representing whether vies_valid is relevant for selecting a fiscal position on this partner
    perform_vies_validation = fields.Boolean(compute='_compute_perform_vies_validation')
    # We put on inverse because a compute with a dependency to itself is not well managed in the ORM (it should be triggered first)
    country_id = fields.Many2one(inverse="_inverse_vat", store=True)
    vat = fields.Char(inverse="_inverse_vat", store=True)

    @api.model
    def _run_vat_checks(self, country, vat, partner_name='', validation='error'):
        """ OVERRIDE """
        if not country or not vat:
            return vat, False
        if len(vat) == 1:
            if vat == '/' or not validation:
                return vat, False
            if validation == 'setnull':
                return '', False
            if validation == 'error':
                raise ValidationError(_("To explicitly indicate no (valid) VAT, use '/' instead. "))
        vat_prefix, vat_number = self._split_vat(vat)

        if vat_prefix == 'EU' and country not in self.env.ref('base.europe').country_ids:
            # Foreign companies that trade with non-enterprises in the EU
            # may have a VATIN starting with "EU" instead of a country code.
            return vat, False

        do_eu_check = False
        prefixed_country = ''
        eu_prefix_country_group = self.env['res.country.group'].search([('code', '=', 'EU_PREFIX')], limit=1)
        country_code = EU_EXTRA_VAT_CODES_INV.get(vat_prefix, vat_prefix)
        if country_code in eu_prefix_country_group.country_ids.mapped('code'):
            if 'EU_PREFIX' in country.country_group_codes and vat_prefix:
                vat = vat_number
                prefixed_country = vat_prefix
            else:
                do_eu_check = True

View on GitHub (pinned to 1e661df964)

Solutions

  1. Use '/' as the vat value to explicitly record that the partner has no (valid) VAT number.
  2. Leave the vat field empty (falsy) instead of writing a placeholder character — empty values skip validation entirely.
  3. For bulk imports where the source data is dirty, pre-clean single-character VATs to '' or '/' before writing to res.partner.

Example fix

# before
partner.write({'vat': '-'})  # ValidationError

# after
partner.write({'vat': '/'})  # explicit 'no VAT' sentinel
Defensive patterns

Strategy: validation

Validate before calling

def normalize_vat(vat):
    if vat and len(vat) == 1 and vat != '/':
        return ''  # or '/' to explicitly mark 'no VAT'
    return vat

partner.write({'vat': normalize_vat(raw_vat)})

Try / catch

from odoo.exceptions import ValidationError
try:
    partner.write({'vat': raw})
except ValidationError:
    partner.write({'vat': '/'})  # explicit no-VAT sentinel

Prevention

When it happens

Trigger: Setting res_partner.vat to a single non-'/' character (e.g. '0', 'x', '-') while the partner has a country with VAT validation active and validation='error' (the default on create/write of vat).

Common situations: Data imports or CSV syncs that map an empty/placeholder VAT column to a stray character; users typing '-' or '.' to mean 'none'; connectors pushing partial values.

Related errors


AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15). Data as JSON: /api/errors/4cfa966ee81d8b7c. Report an issue: GitHub.