できない.dev

Python で「json.decoder.JSONDecodeError: Expecting value」が解消できない

JSONDecodeError: Expecting value: line 1 column 1 (char 0) は、空文字列や JSON でない文字列を json.loads() に渡したときに出る。
まずデコード対象の中身を確認するのが近道。

公開: 更新:

実行例あり(2026-08-30 に実環境で検証)

要約

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) は、json.loads() に渡した文字列が JSON として始まっていないときの定番エラーです。

char 0 は「先頭からおかしい」という意味で、ほとんどの場合は中身が空、または HTML などの別物です。

まず渡している文字列そのものを覗くのが最短です。

実行例

空文字列を json.loads に渡すと Expecting value: line 1 column 1 (char 0) で落ち、char 0 という位置がそのまま「先頭に値が無い」ことを示す。
JSON のつもりで受け取った中身が HTML だった場合も同じ char 0 になるため、e.doc[:80] を出して先頭を見れば <!DOCTYPE html> が返ってきていたと一目で分かる。

$ python bad.py
Traceback (most recent call last):
  File "/tmp/tmp.UHbL8Im6IK/bad.py", line 3, in <module>
    json.loads(text)
  File "/usr/local/lib/python3.12/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/decoder.py", line 338, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/decoder.py", line 356, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
$ echo $?
1
$ python bad_html.py
JSONDecodeError: Expecting value: line 1 column 1 (char 0)
先頭80文字 : '<!DOCTYPE html><html>error page</html>'
$ python solve_repr.py
repr : '{"name": "alice"}'
data : {'name': 'alice'}
$ python solve_load.py
loaded: {'ok': True}

— 2026-08-30 時点の出力

検証環境

検証日
実行環境
python:3.12Debian GNU/Linux 13 (trixie)
バージョン
  • Python 3.12.14
  • Git 2.47.3

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. レスポンス本文が空のまま json.loads() に渡している。
  2. API が JSON ではなく HTML のエラーページや空ボディを返している。
  3. ファイルを読まず、パス文字列やファイルオブジェクトを誤って渡している。
  4. 末尾カンマやシングルクオートなど、JSON として不正な文字列を渡している。

解決策

1. 中身を確認する

パースの前に、対象を repr で表示して空や HTML でないか確かめます。

print(repr(text))      # '' や '<!DOCTYPE html>...' ならパース以前の問題
data = json.loads(text)

2. HTTP エラーを先に弾く

requests では、ステータス確認のあとに .json() を呼びます。

resp = requests.get(url)
resp.raise_for_status()   # 4xx/5xx をここで例外に
data = resp.json()

3. ファイルは load で読む

パス文字列を loads に渡すのは誤りです。
開いてから load を使います。

with open(path, encoding="utf-8") as f:
    data = json.load(f)

捕捉して原因を残すなら、except json.JSONDecodeError as e: で e.pos と e.doc[:80] をログに出すと切り分けが速くなります。

この記事は役立ちましたか?