{"id":"1e7f809e62723a1d","repo":"psycopg/psycopg2","slug":"cannot-switch-from-manual-field-numbering-to-autom","errorCode":null,"errorMessage":"cannot switch from manual field numbering to automatic","messagePattern":"cannot switch from manual field numbering to automatic","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/sql.py","lineNumber":248,"sourceCode":"                raise ValueError(\"no format specification supported by SQL\")\n            if conv:\n                raise ValueError(\"no format conversion supported by SQL\")\n            if pre:\n                rv.append(SQL(pre))\n\n            if name is None:\n                continue\n\n            if name.isdigit():\n                if autonum:\n                    raise ValueError(\n                        \"cannot switch from automatic field numbering to manual\")\n                rv.append(args[int(name)])\n                autonum = None\n\n            elif not name:\n                if autonum is None:\n                    raise ValueError(\n                        \"cannot switch from manual field numbering to automatic\")\n                rv.append(args[autonum])\n                autonum += 1\n\n            else:\n                rv.append(kwargs[name])\n\n        return Composed(rv)\n\n    def join(self, seq):\n        \"\"\"\n        Join a sequence of `Composable`.\n\n        :param seq: the elements to join.\n        :type seq: iterable of `!Composable`\n\n        Use the `!SQL` object's *string* to separate the elements in *seq*.\n        Note that `Composed` objects are iterable too, so they can be used as","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/sql.py#L230-L266","documentation":"Raised by SQL.format() (lib/sql.py:248) - the mirror of error [22]. It fires when a manually-numbered placeholder ({0}) was already seen (autonum set to None) and then an auto-numbered ({}) placeholder follows. The library forbids switching numbering style mid-template, exactly like str.format.","triggerScenarios":"A template like sql.SQL(\"select {0} from {}\").format(a, b) - {0} puts it in manual mode, then {} tries to resume automatic numbering. Also sql.SQL(\"{1} {}\").format(...).","commonSituations":"Same as [22]: partial edits, merged fragments, copy-paste between queries with different placeholder conventions.","solutions":["Make all placeholders explicitly numbered ({0}, {1}, ...) consistently.","Or make all placeholders automatic ({}) and rely on positional arg order.","Use named placeholders ({tbl}, {col}) with kwargs to remove ambiguity entirely."],"exampleFix":"// before\nq = sql.SQL(\"select {0} from {}\").format(sql.Identifier('c'), sql.Identifier('t'))\n// after\nq = sql.SQL(\"select {0} from {1}\").format(sql.Identifier('c'), sql.Identifier('t'))","handlingStrategy":"validation","validationCode":"import string\n\ndef template_numbering_is_consistent(template: str) -> bool:\n    seen_auto = seen_manual = False\n    for _pre, name, _spec, _conv in string.Formatter().parse(template):\n        if not name:\n            continue\n        if name.isdigit():\n            seen_manual = True\n        else:\n            seen_auto = True\n        if seen_auto and seen_manual:\n            return False\n    return True\n\nassert template_numbering_is_consistent(tpl)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one numbering style per template and stick to it.","Prefer named placeholders with kwargs to sidestep the auto/manual rule.","After merging SQL fragments, re-scan the combined template for mixed styles."],"tags":["psycopg2","sql-composition","format","valueerror"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}