psycopg/psycopg2 · error · TypeError

context must be a connection or a cursor

Error message

context must be a connection or a cursor

What it means

Raised by Literal.as_string() (lib/sql.py:385) when the context argument is neither a psycopg2 connection nor a cursor. Literal must adapt the wrapped Python value through psycopg2's adaptation machinery, which needs the connection's encoding and registered type adapters - hence a live connection (or a cursor, which exposes .connection) is mandatory. (Note: SQL.as_string and Identifier.as_string ignore context, so this only bites Literal rendering.)

Source

Thrown at lib/sql.py:385

        >>> s2 = sql.Literal("ba'r")
        >>> s3 = sql.Literal(42)
        >>> print(sql.SQL(', ').join([s1, s2, s3]).as_string(conn))
        'foo', 'ba''r', 42

    """
    @property
    def wrapped(self):
        """The object wrapped by the `!Literal`."""
        return self._wrapped

    def as_string(self, context):
        # is it a connection or cursor?
        if isinstance(context, ext.connection):
            conn = context
        elif isinstance(context, ext.cursor):
            conn = context.connection
        else:
            raise TypeError("context must be a connection or a cursor")

        a = ext.adapt(self._wrapped)
        if hasattr(a, 'prepare'):
            a.prepare(conn)

        rv = a.getquoted()
        if isinstance(rv, bytes):
            rv = rv.decode(ext.encodings[conn.encoding])

        return rv


class Placeholder(Composable):
    """A `Composable` representing a placeholder for query parameters.

    If the name is specified, generate a named placeholder (e.g. ``%(name)s``),
    otherwise generate a positional placeholder (e.g. ``%s``).

View on GitHub (pinned to 3a6d9d6ddc)

Solutions

  1. Pass an actual psycopg2 connection or cursor to as_string(): sql.Literal(v).as_string(conn).
  2. If you only need static placeholder text, use SQL/Identifier/Placeholder whose as_string ignores context.
  3. In tests, use a real (possibly in-memory or temp-DB) connection or avoid rendering Literals offline.

Example fix

// before
lit = sql.Literal(42).as_string(None)
// after
lit = sql.Literal(42).as_string(conn)
Defensive patterns

Strategy: type-guard

Validate before calling

from psycopg2 import extensions

def render_literal(value, ctx):
    if not isinstance(ctx, (extensions.connection, extensions.cursor)):
        raise TypeError('a psycopg2 connection or cursor is required to render a Literal')
    return sql.Literal(value).as_string(ctx)

Type guard

from psycopg2 import extensions

def is_literal_context(ctx) -> bool:
    return isinstance(ctx, (extensions.connection, extensions.cursor))

Prevention

When it happens

Trigger: sql.Literal(42).as_string(None), .as_string('conn'), .as_string(''), or omitting the argument. Also calling as_string on a Literal during offline/unit-test rendering with no live connection.

Common situations: Pre-rendering SQL templates outside any connection scope; passing the wrong object (a string DSN, a pool wrapper); unit tests that try to render Literals without a real psycopg2 connection.

Related errors


AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04). Data as JSON: /data/errors/d195ac29b563f913.json. Report an issue: GitHub.