Quick Answer
The decoder received an empty string, HTML, plain text, or otherwise invalid JSON at character zero. Check the response status, content type, and trimmed body before calling json.loads(); for a file, check that it exists and is not empty.

JSONDecodeError: Expecting value: line 1 column 1 (char 0) means Python tried to parse JSON, but the first character did not start a valid JSON value. In practice, the input is often empty, HTML, a plain-text error message, or a file that was not written as JSON.
Python’s json module documentation explains json.loads(), json.load(), and JSONDecodeError. The fix is not to catch every error and move on. First inspect what you are actually passing into the JSON parser.
Reproduce the error
The smallest example is an empty string. There is no object, array, string, number, Boolean, or null value for the parser to read, so parsing fails at character position zero.
import json
try:
data = json.loads("")
except json.JSONDecodeError as error:
print(error)
print(error.lineno, error.colno, error.pos)
The position data is useful. line 1 column 1 points to the start of the input. If the error points later in the file, you likely have a syntax problem after some valid JSON has already been read.

Check for empty API responses
Many API failures are not JSON parser problems. A server may return a 204 response, an empty body, or an HTML error page. Check the status code, content type, and body text before calling json.loads().
import json
class Response:
status_code = 204
headers = {"content-type": "application/json"}
text = ""
response = Response()
if response.status_code == 204 or not response.text.strip():
data = None
else:
data = json.loads(response.text)
print(data)
For real HTTP clients, apply the same checks to the response object you receive. If the response is empty by design, do not parse it as JSON.
Check content type before parsing
Another common cause is receiving HTML while expecting JSON. A login page, proxy error, or rate-limit page may start with <html>, which is not a JSON value.
import json
headers = {"content-type": "text/html; charset=utf-8"}
body = "<html><title>Error</title></html>"
content_type = headers.get("content-type", "")
if "application/json" not in content_type:
raise ValueError(f"Expected JSON, got {content_type}")
data = json.loads(body)
print(data)
This error message is more helpful than the raw decoder error because it tells you the upstream response was the wrong format. Log a short preview of the body when debugging, but avoid logging private payloads.

Read JSON files safely
For files, check that the path exists and that the file is not empty before loading it. This separates file problems from JSON syntax problems.
import json
from pathlib import Path
path = Path("settings.json")
if not path.exists():
raise FileNotFoundError(path)
if path.stat().st_size == 0:
data = {}
else:
with path.open(encoding="utf-8") as file:
data = json.load(file)
print(data)
The check if a file exists guide covers file checks in more detail. If you are writing bytes or reading binary content before parsing, the byte-like object required guide may also help.
Show invalid JSON clearly
When the input is not empty, print a small preview and the decoder location. That usually reveals single quotes, trailing commas, comments, or other syntax that JSON does not allow.
import json
text = "{'name': 'Python Pool'}"
try:
data = json.loads(text)
except json.JSONDecodeError as error:
preview = text[max(error.pos - 20, 0):error.pos + 20]
print(f"line={error.lineno} column={error.colno} pos={error.pos}")
print(preview)
JSON requires double quotes around object keys and string values. Python dictionary syntax is similar, but it is not the same format. If you need to compare valid JSON files, see the jsondiff in Python guide.
Handle a UTF-8 byte order mark
Some files begin with a byte order mark. If that character appears at the start of the decoded text, it can confuse JSON parsing. Open the file with utf-8-sig to remove it while reading.
import json
from pathlib import Path
def load_json_with_sig(path):
text = Path(path).read_text(encoding="utf-8-sig")
if not text.strip():
return None
return json.loads(text)
sample = chr(0xFEFF) + '{"ok": true}'
print(json.loads(sample.lstrip(chr(0xFEFF))))
Use this only when you know files may include that marker. For ordinary UTF-8 JSON, encoding="utf-8" is enough.

Create a safe helper
A small helper can handle the most common cases: empty input, wrong content type, and decoder errors with useful context. The helper should return a known fallback only for truly empty input. For malformed JSON, it should raise a clearer exception and keep the original JSONDecodeError as the cause.
This does not hide bad JSON. It gives the caller a clearer failure path and treats truly empty input as a known case. If you need optional imports around JSON-related packages, the Python conditional import guide shows safe import patterns.
Prevent the error
Preventing this error is mostly about boundaries. Do not parse a response until you know it succeeded. Do not parse a file until you know it exists and has content. Do not assume a command-line tool, web API, or cache wrote JSON just because the filename ends in .json. A small validation step before parsing saves a longer debugging session later.

Quick checklist
When you see line 1 column 1 char 0, check the raw text first. Is it empty? Is it HTML? Is it a server error? Is the content type JSON? Is the file path correct? Is the file blank? Is the text valid JSON with double quotes? Answer those questions before changing parser code.
The decoder error is a symptom. The fix is usually at the boundary where data is fetched, read, or written. Validate that boundary, then parse only when the input is actually JSON.
Inspect the Raw Input Without Logging Secrets
When debugging an API or file, inspect a short, redacted preview rather than printing a complete token or user payload. A body that starts with <html>, an empty string, or a proxy message points to an upstream problem rather than malformed JSON generated by Python.
def describe_body(body):
preview = body.strip()[:120]
return {"empty": not bool(preview), "preview": preview}
print(describe_body(response.text))
Once the input is confirmed to be JSON, parse it and handle JSONDecodeError at the boundary so the rest of the application receives a clear failure or a documented fallback.
Frequently Asked Questions
What does JSONDecodeError at line 1 column 1 mean?
Python found no valid JSON value at the start of the input. The body is commonly empty, HTML, plain text, or malformed before the first character.
Why does an API return JSONDecodeError when the endpoint looks correct?
The endpoint may return a login page, rate-limit page, server error, or 204 empty response. Check status_code, content-type, and response.text before parsing.
How do I handle an empty JSON file?
Check that the file exists and has non-whitespace content before json.load(). Decide whether an empty file is an error or should map to a documented default value.
Should I catch JSONDecodeError and ignore it?
No. Catch it at the input boundary, record a safe diagnostic, and either return a clear error or apply an intentional fallback. Ignoring it can hide an upstream outage.