{"id":"d195ac29b563f913","repo":"psycopg/psycopg2","slug":"context-must-be-a-connection-or-a-cursor","errorCode":null,"errorMessage":"context must be a connection or a cursor","messagePattern":"context must be a connection or a cursor","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/sql.py","lineNumber":385,"sourceCode":"        >>> s2 = sql.Literal(\"ba'r\")\n        >>> s3 = sql.Literal(42)\n        >>> print(sql.SQL(', ').join([s1, s2, s3]).as_string(conn))\n        'foo', 'ba''r', 42\n\n    \"\"\"\n    @property\n    def wrapped(self):\n        \"\"\"The object wrapped by the `!Literal`.\"\"\"\n        return self._wrapped\n\n    def as_string(self, context):\n        # is it a connection or cursor?\n        if isinstance(context, ext.connection):\n            conn = context\n        elif isinstance(context, ext.cursor):\n            conn = context.connection\n        else:\n            raise TypeError(\"context must be a connection or a cursor\")\n\n        a = ext.adapt(self._wrapped)\n        if hasattr(a, 'prepare'):\n            a.prepare(conn)\n\n        rv = a.getquoted()\n        if isinstance(rv, bytes):\n            rv = rv.decode(ext.encodings[conn.encoding])\n\n        return rv\n\n\nclass Placeholder(Composable):\n    \"\"\"A `Composable` representing a placeholder for query parameters.\n\n    If the name is specified, generate a named placeholder (e.g. ``%(name)s``),\n    otherwise generate a positional placeholder (e.g. ``%s``).\n","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/sql.py#L367-L403","documentation":"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.)","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass an actual psycopg2 connection or cursor to as_string(): sql.Literal(v).as_string(conn).","If you only need static placeholder text, use SQL/Identifier/Placeholder whose as_string ignores context.","In tests, use a real (possibly in-memory or temp-DB) connection or avoid rendering Literals offline."],"exampleFix":"// before\nlit = sql.Literal(42).as_string(None)\n// after\nlit = sql.Literal(42).as_string(conn)","handlingStrategy":"type-guard","validationCode":"from psycopg2 import extensions\n\ndef render_literal(value, ctx):\n    if not isinstance(ctx, (extensions.connection, extensions.cursor)):\n        raise TypeError('a psycopg2 connection or cursor is required to render a Literal')\n    return sql.Literal(value).as_string(ctx)","typeGuard":"from psycopg2 import extensions\n\ndef is_literal_context(ctx) -> bool:\n    return isinstance(ctx, (extensions.connection, extensions.cursor))","tryCatchPattern":null,"preventionTips":["Always thread the live connection/cursor through to any code that renders Literals.","For offline rendering, prefer SQL/Identifier/Placeholder whose as_string ignores context.","In tests, stand up a real (temp) connection rather than passing None."],"tags":["psycopg2","sql-composition","literal","typeerror"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}