On this page
Odoo Automated Action: why imports fail and how to fix IMPORT_NAME
Short answer: ValueError: forbidden opcode(s) in "...": IMPORT_NAME means your code contains an import statement, which Odoo's safe_eval forbids. Delete the import line and use what Odoo already preloads: env, datetime, dateutil, time, user.
Environment
- Odoo 17 / 18 / 19
- Settings > Technical > Automation > Automated Actions > Execute Python Code
Reproducer
import datetime
for record in records:
record.write({'x_reminder_date': datetime.date.today()})
Produces:
ValueError: forbidden opcode(s) in "<string>": IMPORT_NAME
Why Odoo forbids imports
safe_eval only allows names Odoo puts into the execution context. An import would pull in arbitrary external modules outside Odoo's control, so it is removed at opcode level — together with STORE_ATTR and other risky opcodes.
The good news: everything commonly needed is already preloaded, so no import is required.
Names available without importing
| Name | Used for | Example |
|---|---|---|
env | Accessing other models | env['res.partner'].search([]) |
records / record | Triggering records | for record in records: |
user | Current user | user.id, user.company_id |
datetime | Dates and times | datetime.date.today() |
dateutil | Relative date math | dateutil.relativedelta.relativedelta(days=7) |
time | Time formatting | time.strftime('%Y-%m-%d') |
The fix: rewrite without imports
The example above is fixed by simply deleting the import line:
for record in records:
record.write({'x_reminder_date': datetime.date.today()})
A deadline 7 days out:
for record in records:
deadline = datetime.date.today() + dateutil.relativedelta.relativedelta(days=7)
record.write({'x_deadline': deadline})
Looking up a partner and linking it:
for record in records:
partner = env['res.partner'].search([('email', '=', record.x_contact_email)], limit=1)
if partner:
record.write({'partner_id': partner.id})
Common mistakes
from datetime import date— stillIMPORT_NAME. Every form of import is blocked.import jsonfor data conversion — find another way, e.g. pass strings throughChar/Textfields directly. If you genuinely need it, move the logic into a custom addon module.- External libraries like
requests— impossible inside Automated Actions. Use Odoo's webhook action or a custom module for external API calls. - Confusing it with
STORE_ATTR— if fixing the import revealsSTORE_ATTR, your code still assigns fields directly. Switch torecord.write()as shown in Fix forbidden opcode STORE_ATTR with record.write().
When to stop using Automated Actions and write a module instead
- You need external libraries (
requests,pandas, others). - Logic exceeds roughly 30–40 lines and becomes hard to read.
- You need real unit tests or version control.
Automated Actions fit short if-then-write jobs. Beyond that, a custom addon is cheaper long-term.
References
- Odoo 19 Automated Actions docs: https://www.odoo.com/documentation/19.0/applications/studio/automated_actions.html
Related notes
- Odoo Automated Action: Fix forbidden opcode STORE_ATTR with record.write()
- Odoo QWeb: fixing PDF page breaks with real examples
- All Odoo notes