Odoo upgrade error
res.country.state: the values for the fields 'country_id, code' already exist
Last updated: October 2026
The base module cannot load its list of states because your database already has a state with the same country and code that Odoo does not recognize as its own.
This page is part of our guide on how to upgrade Odoo Community Edition. It explains what triggers the error, how to fix it by hand, and what an automated migration does about it.
The error
Exception: Module loading base failed: file base/data/res.country.state.csv could not be processed:
The values for the fields 'country_id, code' already exist (they are probably 'Country, State Code' in the current model).Some localization modules ship states too, so the file in the message can also be an l10n_* data file.
- Constraint
unique(country_id, code)onres.country.state(Odoo 15 to 19)- Upgrade hops
- Any; we have traced it on 16→17
- Seen when
basereloads its data files during-u all
When you see it
Odoo ships every country's states in base/data/res.country.state.csv, each with an external ID such as base.state_ch_zg. On an update, Odoo looks up each line by that ID. If the ID exists it updates the row; if not, it creates a new row. Creating fails when a row with the same country and code already exists, and the whole base module load stops.
Root cause
The database has the state, but without the external ID Odoo uses for it. The row has become an orphan. This happens when a state was deleted and created again by hand, imported from a spreadsheet, created before Odoo added it to its own data, or when a clean-up tool removed rows from ir_model_data. In the case we traced, two of about 1,400 states had no external ID: one Swiss canton with the standard code, and one hand-made state.
List the states without an external ID:
SELECT s.id, c.code AS country, s.code, s.name
FROM res_country_state s
JOIN res_country c ON c.id = s.country_id
LEFT JOIN ir_model_data d
ON d.model = 'res.country.state' AND d.res_id = s.id
WHERE d.id IS NULL
ORDER BY c.code, s.code;How to fix it
Give each orphan the external ID Odoo uses for its country and code. Find that ID in the id column of res.country.state.csv in the base module of the version you are upgrading to. Linking keeps the row, and every partner address that points at it, and turns Odoo's create into an update.
Before you change anything: Back up the database before running any UPDATE or INSERT below, and try it on a copy first. These statements change data directly, outside the Odoo ORM.
-- example: link orphan state 123 (Switzerland, ZG) to base.state_ch_zg
INSERT INTO ir_model_data (module, name, model, res_id, noupdate)
VALUES ('base', 'state_ch_zg', 'res.country.state', 123, false);Before inserting, make sure the external ID does not already exist and point at another row. If it does, you have two rows for the same state: move the references from one to the other before deleting the duplicate.
A hand-made state whose code does not match any state Odoo ships can stay as it is; it does not collide.
How OCU handles it
On the hops to Odoo 17, 18 and 19, OCU generates a linker from the target version's data files, including the base CSV and the localization modules that ship states. Before the upgrade it finds each existing state with the same country and code as a shipped record but without an external ID, and links it to the target's ID. The data load then updates the row instead of creating a duplicate, and your addresses keep pointing at the same state.
The 15→16 hop does not run this linker yet. For a 15→16 failure with this message, use the manual fix above.
A free test migration shows whether your database runs into this or any other upgrade error before you change anything on your production server.
Frequently asked questions
- Can I delete the duplicate state instead?
- Only if nothing references it. Partner addresses, companies, warehouses and tax rules can point at a state. Linking the existing row to Odoo's external ID keeps all of those references intact.
- Why did this work before the upgrade?
- The unique constraint has existed for many versions. The collision only happens when Odoo tries to create its own copy of the state, which it does when it reloads the base data during an upgrade and cannot find the state by external ID.
Related
Find upgrade errors before your production run
Upload a backup and run a free test migration. You get the upgraded database, the full log and every warning, with no change to your live system.
Start your free test migration