{"id":"263d3ee154dbaff3","repo":"psycopg/psycopg2","slug":"composed-elements-must-be-composable-got-i-r-in","errorCode":null,"errorMessage":"Composed elements must be Composable, got {i!r} instead","messagePattern":"Composed elements must be Composable, got (.+?) instead","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/sql.py","lineNumber":108,"sourceCode":"    The object is usually created using `!Composable` operators and methods.\n    However it is possible to create a `!Composed` directly specifying a\n    sequence of `!Composable` as arguments.\n\n    Example::\n\n        >>> comp = sql.Composed(\n        ...     [sql.SQL(\"insert into \"), sql.Identifier(\"table\")])\n        >>> print(comp.as_string(conn))\n        insert into \"table\"\n\n    `!Composed` objects are iterable (so they can be used in `SQL.join` for\n    instance).\n    \"\"\"\n    def __init__(self, seq):\n        wrapped = []\n        for i in seq:\n            if not isinstance(i, Composable):\n                raise TypeError(\n                    f\"Composed elements must be Composable, got {i!r} instead\")\n            wrapped.append(i)\n\n        super().__init__(wrapped)\n\n    @property\n    def seq(self):\n        \"\"\"The list of the content of the `!Composed`.\"\"\"\n        return list(self._wrapped)\n\n    def as_string(self, context):\n        rv = []\n        for i in self._wrapped:\n            rv.append(i.as_string(context))\n        return ''.join(rv)\n\n    def __iter__(self):\n        return iter(self._wrapped)","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/sql.py#L90-L126","documentation":"Raised by Composed.__init__ (lib/sql.py:107-109) when any element in the sequence passed to Composed(...) is not an instance of Composable. Composed is a composition of Composable parts (SQL, Identifier, Literal, Placeholder, or another Composed); raw strings, bytes, numbers, or None are rejected because they would bypass proper escaping and produce unsafe or malformed SQL.","triggerScenarios":"Constructing sql.Composed([\"SELECT\", sql.Identifier('t')]) or sql.Composed([sql.SQL('a'), 'b', 1]). Also triggered indirectly via Composable.__add__ (which builds Composed) and SQL.format() which assembles a Composed from template parts.","commonSituations":"Developers mix raw strings into Composed lists expecting automatic wrapping, or pass user-supplied values directly instead of wrapping them with sql.Literal. Confusion between sql.SQL (constant template, no escaping) and sql.Literal (escaped value).","solutions":["Wrap every element in the appropriate Composable: sql.SQL for constant snippets, sql.Identifier for names, sql.Literal for values, sql.Placeholder for parameters.","Use sql.SQL(', ').join([...]) which accepts Composables and is the idiomatic way to build lists.","Never put raw user input directly into a Composed; always go through Literal or a placeholder."],"exampleFix":"// before\ncomp = sql.Composed([\"SELECT * FROM \", sql.Identifier('t')])\n// after\ncomp = sql.Composed([sql.SQL(\"SELECT * FROM \"), sql.Identifier('t')])","handlingStrategy":"type-guard","validationCode":"for el in seq:\n    assert isinstance(el, Composable), f'element {el!r} is not Composable'\ncomp = sql.Composed(seq)","typeGuard":"from psycopg2.sql import Composable\ndef all_composable(seq) -> bool:\n    return all(isinstance(x, Composable) for x in seq)","tryCatchPattern":"try:\n    comp = sql.Composed(seq)\nexcept TypeError as e:\n    if 'must be Composable' in str(e):\n        seq = [sql.SQL(x) if isinstance(x, str) else x for x in seq]\n        comp = sql.Composed(seq)\n    else: raise","preventionTips":["Never put raw strings/bytes/numbers into a Composed list.","Use sql.SQL(', ').join(...) which enforces Composable inputs idiomatically.","Wrap user values with sql.Literal and names with sql.Identifier."],"tags":["sql","composable","type-error","sql-injection","type-guard"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}