Error [is a named parameter, but you supplied a sequence]

`sqlite3.ProgrammingError: Binding 1 (':value') is a named parameter` の直し方(Python 3.14)

FIX SUMMARY verified
Applies when
python@sha256:b877e50bd90de10af8d82c57a022fc2e0dc731c5320d762a27986facfc3355c1named placeholder (:name / @name / $name — all three) bound with a non-dict sequence; DeprecationWarning in 3.12/3.13 (its text names 3.14) becomes sqlite3.ProgrammingError in 3.14. CPython rejects before sqlite3_bind_*, so the boundary is CPython, not SQLite (measured: sqlite3.sqlite_version is 3.46.1 on all four official python images)

Verified: reproduced in python@sha256:b877e50bd90de10af8d82c57a022fc2e0dc731c5320d762a27986facfc3355c1, then the is a named parameter, but you supplied a sequence signature was gone after the fix (exit 0).

sqlite3.ProgrammingError という名前が付いていますが、この例外を投げているのは SQLite ではありません。

sqlite3.ProgrammingError: Binding 1 (':value') is a named parameter, but you supplied a sequence which requires nameless (qmark) placeholders.

該当するのは、名前つきの placeholder に tuple や list を渡しているコードです。

connection.execute("SELECT :value", (42,))     # 3.14 で ProgrammingError

3.13 では同じ行が動きます。Python 3.14 は、名前つき placeholder に名前を持たない引数(tuple・list)を渡す組み合わせを、警告からエラーに変えました。 直すには、placeholder の名前をキーにした dict を渡します

connection.execute("SELECT :value", {"value": 42})   # 3.11〜3.14 すべてで通る

名前つき placeholder は :name だけではありません。 SQLite は :name / @name / $name の 3 つを名前つきとして扱い、3 つとも同じように落ちます(実測)。: だけを検索して直すと、@$ が残ります。

3.12・3.13 は、落ちる版を名指しして予告していた

この組み合わせは 3.12 から DeprecationWarning になっていて、その警告文は落ちる版まで名指ししていました

DeprecationWarning: Binding 1 (':value') is a named parameter, but you supplied a sequence
which requires nameless (qmark) placeholders.
Starting with Python 3.14 an sqlite3.ProgrammingError will be raised.

3.14 のエラー文は、この警告文から最後の一文(予告)を取り除いたものです。

Pythonexecute("SELECT :value", (42,))
3.11通る(警告なし)
3.12DeprecationWarning(上の全文)を出して通る(終了コード 0)
3.13同上(終了コード 0)
3.14sqlite3.ProgrammingError(終了コード 1)

この予告が届いたかどうかは、その execute() がどのモジュールにあったかで決まります。CPython の既定の警告フィルタは、DeprecationWarning発生元が __main__ のときだけ表示し、それ以外のモジュールでは抑制するからです(既定の警告フィルタ)。app.py に直接書いた execute() なら 3.13 は -W なしでも警告を出していましたが(実測)、SQL はたいていリポジトリ層やモデルのモジュールにあるので、そこで出る分は既定では 1 行も表示されません(実測)。

3.13 のうちに洗い出すなら、抑制を外して実行します。

python -W always::DeprecationWarning app.py
python -W always::DeprecationWarning -m pytest

警告は execute() を呼ぶたびに評価されるので、テストが通る経路の分はここで出そろいます(asyncio の同種の警告と違い、1 回目で状態が変わって黙るということはありません)。ただし、テストが踏まない SQL の行は出てきません:@$ の検索と併用してください。

直し方:placeholder の名前をキーにした dict を渡す

# 落ちる
connection.execute("INSERT INTO t VALUES (:a, :b)", [1, 2])

# 通る
connection.execute("INSERT INTO t VALUES (:a, :b)", {"a": 1, "b": 2})

この修正は前方にも後方にも安全です。dict を渡す形は 3.11 から 3.14 まで、どの版でも同じように通ります(実測)。3.13 のまま先に直しておいて構いません。

executemany() も同じ検査を通ります。 行のリストを tuple で渡していると、同じ ProgrammingError になります。

# 落ちる
connection.executemany("INSERT INTO t VALUES (:a, :b)", [(1, 2), (3, 4)])

# 通る
connection.executemany("INSERT INTO t VALUES (:a, :b)", [{"a": 1, "b": 2}, {"a": 3, "b": 4}])

dict に余分なキーが混ざっているのは 3.14 でも許されます(不足しているときだけ、You did not supply a value for binding parameter :value. という別のエラーになります。これは以前からの動作です)。つまり、行を表す dict をそのまま渡し、SQL 側で使う名前だけを拾わせる書き方は、これまでどおり使えます。

落ちる組み合わせ・落ちない組み合わせ

CPython の検査は、こう書かれています——placeholder に ? 以外で始まる名前が付いていて、引数が dict でなければ拒否する。SQLite が名前を付けるのは :name / @name / $name の 3 つで、?1 にも "?1" という名前が付きますが、CPython は ? で始まる名前を検査から外しています。だから ??1 は tuple のままで構いません。

SQL の placeholder渡す引数3.133.14
:nametuple / list通る(警告)ProgrammingError
@nametuple / list通る(警告)ProgrammingError
$nametuple / list通る(警告)ProgrammingError
:name / @name / $namedict通る通る
?tuple / list通る通る
?1(番号つき)tuple / list通る通る
?dictProgrammingErrorProgrammingError

名前つきは 3 形あります。 :name だけを検索して置き換えると、@name$name を使っている行が残ります(エラー文の Binding 1 (':value') の部分が、それぞれ '@value''$value' になります)。@ 記法は SQL Server 系から移してきたコードでよく残っています。

「tuple や list」も、正確には「dict でない sequence」です。 namedtuple を渡していても落ちます(実測)。逆に、dict のサブクラスは dict として扱われて通ります(実測)。

表の最下段にあるとおり、逆向きの組み合わせ(? に dict)は 3.11 でもエラーでした。「placeholder と引数の型が合わなければ拒否する」という規則自体は、以前からありました。片方向だけが見逃されていて、3.14 でそれが閉じた、という形です。

名前つきと ? が混ざった SQL は、引数を変えても直りません

1 つの SQL に名前つきと ? が両方あると、どちらの引数型でも拒否されます(実測)。

connection.execute("SELECT :a, ?", (1, 2))     # 3.14: Binding 1 (':a') is a named parameter ...
connection.execute("SELECT :a, ?", {"a": 1})   # 3.13 も 3.14 も: Binding 2 has no name, but you supplied a dictionary

sequence は :a の検査で弾かれ、dict は ? に名前が無いので弾かれます。引数の型を変えて直す道はありません。 SQL 側を、名前つきだけか ? だけかに統一してください。この形は 3.13 では tuple で通っていたので、3.14 で初めて表に出てきます。

SQLite を上げ下げしても直りません

エラーの名前が sqlite3.ProgrammingError なので、SQLite 側の変更に見えます。実際は違います。

公式 python イメージsqlite3.sqlite_version
3.113.46.1
3.123.46.1
3.133.46.1
3.143.46.1

4 つとも同じ SQLite です。 挙動を変えたのは CPython の sqlite3 モジュールで、SQL を SQLite に渡す前の引数検査です。SQLite エンジンはこの引数を一度も見ていません。したがって、libsqlite3 を入れ替えても、sqlite3.sqlite_version を確認しても、この件は動きません。境界は CPython の版です。

(この表は公式 python イメージでの実測です。Linux の CPython は SQLite を同梱せず、その環境の libsqlite3 にリンクします——4 つで版が一致したのは、4 つのイメージが同じ Debian ベースだからです。Python の版が SQLite の版を決めているわけではありません(macOS・Windows のインストーラは同梱するので、版ごとに違います)。手元で sqlite3.sqlite_version が版ごとに違っても、上の結論は変わりません——CPython が SQLite に渡す前に弾いているからです。)

切り分け(うまくいかないとき)

  • エラー文の Binding 1 の番号だけ変わって出続ける:SQL 内の別の placeholder が残っています。1 つの execute() に複数の名前つき placeholder があると、最初に見つかった 1 つが報告されます。その SQL の引数を丸ごと dict に変えてください。
  • dict に変えたら Binding 2 has no name, but you supplied a dictionary になった:その SQL に ? が混ざっています。上の節のとおり、引数の型では直りません。SQL 側を統一してください。
  • ORM やクエリビルダの中で落ちる:SQL と引数を組み立てているのはそのライブラリです。ライブラリが名前つき placeholder を生成しながら引数を tuple で渡していると、この検査に当たります。ライブラリを 3.14 対応版に上げる以外の回避策は、利用者側にはありません(SQL も引数もライブラリが作っているため)。上げられないなら 3.13 に留まることになります。自分のコードで execute() を直接呼んでいる箇所を先に潰すと、残りがライブラリ由来だと切り分けられます。
  • You did not supply a value for binding parameter :value. に変わった:dict へ移行できていますが、キーが足りません。キーには記号を付けません:value / @value / $value のどれでも、キーは "value" です)。このエラーは 3.13 でも同じ文言で出るので、3.14 特有ではありません。
  • Binding 1 has no name, but you supplied a dictionary:SQL 側が ? なのに dict を渡しています。3.14 特有ではなく、3.11 でも同じエラーになります。
  • 3.13 では警告も出ない:呼んでいるモジュールが __main__ でないなら、既定では抑制されます。-W always::DeprecationWarning を付けてください。pytest なら filterwarnings の設定が警告を握りつぶしていることもあります。
  • 3.14 の他の変更も先に知っておきたい:同じ 3.14 で、multiprocessing の既定 start methodasyncio.get_event_loop() の暗黙生成 も、3.13 まで通っていた形が落ちるようになりました。

検証環境

  • python@sha256:b877e50b...python:3.14-slim / Python 3.14.6・Linux)、ネットワーク不要
  • 再現:connection.execute("SELECT :value", (42,))sqlite3.ProgrammingError: Binding 1 (':value') is a named parameter で終了コード 1
  • 修正:connection.execute("SELECT :value", {"value": 42}) にすると終了コード 0

再現から修正までは errfix の検証ハーネスが機械的に確認しています。本文の版ごとの表(3.11 は無言で通す/3.12・3.13 は版を名指しした DeprecationWarning つきで通す/3.14 は ProgrammingError)、落ちる組み合わせの表(@name$name も対象?1 は対象外・namedtuple も sequence として落ちる・dict のサブクラスは通る・executemany も同じ検査・dict の余分なキーは許される)、名前つきと ? が混ざった SQL は tuple でも dict でも拒否されること3.13 では -W なしでも __main__ からの呼び出しなら警告が出て、パッケージ側のモジュールからの呼び出しでは出ないこと、4 つのイメージとも sqlite3.sqlite_version が 3.46.1 であることは、いずれも公式 python イメージの 3.11・3.12・3.13・3.14 で実測しています。CPython の判定条件(? で始まる名前を検査から除外する)は CPython のソースに拠ります。

検証(machine-verified)

この修正は python@sha256:b877e50bd90de10af8d82c57a022fc2e0dc731c5320d762a27986facfc3355c1 のバージョン固定コンテナ内で再現し、修正後に is a named parameter, but you supplied a sequence のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs python/sqlite-named-placeholder-sequence
● reproduce sqlite3.ProgrammingError: Binding 1 (':value') is a named parameter present ✓
● apply fix exit 0
● re-run sqlite3.ProgrammingError: Binding 1 (':value') is a named parameter gone ✓
PASS verified · python@sha256:b877e50bd90de10af8d82c57a022fc2e0dc731c5320d762a27986facfc3355c1 · signature gone

確認したのは上のイメージの中だけです。別の環境で直らなかった、記述が違う、という場合は 報告してください(対象と検証イメージは件名・本文に入ります)。