Odoo 16: psycopg2.InterfaceError Cursor Already Closed

The "psycopg2.InterfaceError: Cursor already closed" error in Odoo 16, particularly when using @ormcache, stems from caching database recordsets. This article details the root cause and provides practical solutions for developers.

Binary Buddies · 5 min read · 4 views

The Problem: Stale Cursors in Odoo 16

A recurring issue in Odoo 16, especially when leveraging the @ormcache decorator for performance, is the psycopg2.InterfaceError: Cursor already closed. This error typically surfaces when a method decorated with @ormcache returns an Odoo recordset, and that recordset is subsequently accessed in a new environment or after the original database cursor has been closed. The immediate trigger is often a second call to an onchange event or a re-computation of a stored field that relies on cached data.

The core of the problem lies in the lifecycle of database cursors within Odoo’s web request model. Each HTTP request usually gets its own database cursor. When a request completes, Odoo’s framework is designed to close this cursor. If cached data, such as an Odoo recordset, holds a reference to this now-closed cursor, any attempt to interact with that data (like reading a field value) will fail with the described InterfaceError. This is particularly problematic because @ormcache, being a registry-level cache, persists across multiple HTTP requests, meaning the stale cursor reference can outlive the request it originated from.

Consider a scenario where a stored computed field on a sale.order model depends on data from a custom my_module.contract model. A helper method, find_active_contract, decorated with @ormcache, is used to fetch the relevant contract. The first time the sale.order form is interacted with, the cache is populated with the contract recordset. Upon subsequent interactions within a new request context, the cached recordset is retrieved, but the original database cursor is long gone, leading directly to the psycopg2.InterfaceError when fields on that contract recordset are accessed.

Understanding @ormcache and Environment Scopes

The @ormcache decorator in Odoo provides a powerful mechanism for memoizing function calls at the Odoo registry level. This means the cached results are shared across all workers and persist as long as the Odoo worker process is running or the registry is not reset. It’s a form of Least Recently Used (LRU) cache that operates independently of the Odoo environment associated with a specific HTTP request.

A fundamental misunderstanding arises when @ormcache is used to store Odoo recordsets. Recordsets are intrinsically tied to the Odoo environment (self.env) in which they were created. This environment holds a direct reference to the database cursor active during that request. Caching a recordset effectively caches this environment-bound cursor, which is ephemeral and tied to the request lifecycle.

When the cached recordset is later retrieved by @ormcache in a new request context, the original cursor is no longer valid. Accessing any field on the recordset triggers a database operation, attempting to use the dead cursor, resulting in the psycopg2.InterfaceError: Cursor already closed.

This article draws on Odoo 16: psycopg2.InterfaceError: Cursor already closed on second onchange call and answers to it (1, 2), on the Stack Exchange network, licensed under CC BY-SA 4.0.

  • #interfaceerror
  • #odoo
  • #odoo-16
  • #ormcache
  • #postgresql
  • #psycopg2

Comments

Add a comment
Sign in with Google to join the conversation