If you have hit this, you know the exact shape of it:
json.decoder.JSONDecodeError: Expecting property name enclosed in
double quotes: line 1 column 2 (char 1)Or in a browser:
SyntaxError: Unexpected token ' in JSON at position 1The message is precise but unhelpful, because it describes the symptom rather than the cause. Column 2, character 1 is the very first key of your object. The parser read the opening brace, expected a double quote, and found something else.
Nine times out of ten, what it found was a single quote — because what you actually pasted is a Python dictionary, not JSON.
Why Python dicts and JSON look identical but are not
Print a dictionary in Python and you get something that looks like JSON:
>>> user = {'name': 'Ada', 'active': True, 'score': None}
>>> print(user)
{'name': 'Ada', 'active': True, 'score': None}Braces, colons, commas — it has the right shape. But that is Python's repr, not JSON. Four things differ, and each one breaks a strict JSON parser:
| Python | JSON | What happens if you don't convert |
|---|---|---|
| `'single quotes'` | `"double quotes"` | Expecting property name enclosed in double quotes |
| `True` / `False` | `true` / `false` | Unexpected token T |
| `None` | `null` | Unexpected token N |
| `(1, 2)` tuple | `[1, 2]` array | Unexpected token ( |
JSON's specification is deliberately strict: keys and string values must use double quotes. There is no variant of JSON that accepts single quotes. Anything that appears to is doing a conversion first.
The fastest fix
Paste the dictionary into the Python dict to JSON converter. It handles all four differences in one pass — quotes, booleans, None, and tuples — and gives you valid JSON you can copy straight out.
If you want to verify the result or reformat it afterwards, the JSON formatter validates and pretty-prints it, and will tell you the exact line if anything is still wrong.
Fixing it in Python itself
If this is happening inside a script rather than a one-off paste, the root cause is usually that something upstream used `str()` or `print()` where it should have used `json.dumps()`.
Wrong — produces a Python repr with single quotes:
with open('out.json', 'w') as f:
f.write(str(user)) # {'name': 'Ada', 'active': True}Right — produces valid JSON:
import json
with open('out.json', 'w') as f:
json.dump(user, f) # {"name": "Ada", "active": true}The same applies when returning data from an API handler. If you are building the response body by hand with f-strings, you will produce single-quoted output the moment a dict gets interpolated. Use `json.dumps()` and let it handle escaping.
When you cannot change the source
Sometimes the single-quoted string arrives from a log file, a database column, or a third-party system you do not control. You have two options.
`ast.literal_eval` is the safe one:
import ast, json
raw = "{'name': 'Ada', 'active': True, 'score': None}"
data = ast.literal_eval(raw) # a real Python dict
clean = json.dumps(data) # {"name": "Ada", "active": true, "score": null}`literal_eval` only evaluates literals — strings, numbers, tuples, lists, dicts, booleans, and None. It will refuse function calls and arbitrary expressions, which is exactly what you want for untrusted input.
Do not use `eval()` for this. It executes whatever it is given. A string from a log file is untrusted input, and `eval` on untrusted input is a remote code execution vulnerability, not a parsing convenience.
Other things that produce the same error
Single quotes are the common cause, but not the only one. If your input already uses double quotes and you still get this error, check for:
- A trailing comma. `{"a": 1,}` is valid in Python and JavaScript object literals, and invalid in JSON. The error may point at the closing brace instead.
- Comments. `// like this` or `# like this` are not part of JSON. Config files that "look like JSON" frequently contain them — that is usually JSON5 or JSONC.
- Unquoted keys. `{name: "Ada"}` is a valid JavaScript object literal but not JSON. This is the single most common mistake when copying an object out of browser devtools.
- A BOM or stray whitespace before the opening brace, which shifts the reported column.
- Python's `NaN` and `Infinity`, which `json.dumps` will happily emit by default but which strict parsers reject. Pass `allow_nan=False` to catch this at write time.
The JSON formatter is tolerant about the first three when reading — it will accept trailing commas and comments, then hand you strict output — so it is a quick way to find out which of these you are dealing with.
A note on reading the error position
The character offset is genuinely useful once you know how to read it. `char 1` means the parser had consumed exactly one character — the `{` — before failing. So the problem is at the very first key.
A much larger offset points somewhere specific in your payload. If your JSON is on a single line, paste it into the JSON formatter first; once it is pretty-printed, the line and column in the error map to something you can actually find by eye.
Frequently asked questions
Is single-quoted JSON valid anywhere?
No. RFC 8259 requires double quotes for strings and keys. Some parsers accept single quotes as a lenient extension, but you cannot rely on it, and anything you send to another system will likely be rejected.
Why does JavaScript accept my object but JSON.parse rejects it?
JavaScript object literals and JSON are different grammars. Object literals allow unquoted keys, single quotes, trailing commas, and comments. JSON allows none of those. Copying an object out of devtools gives you the literal, not JSON.
What is the difference between json.dump and json.dumps?
`dump` writes to a file object; `dumps` returns a string. The trailing s stands for string. Both produce valid JSON, unlike `str()`.
Can I convert without a script?
Yes — paste it into the [Python dict converter](/python-dict-converter). It runs in your browser, so the data never leaves your machine, which matters if the payload has anything sensitive in it.
My input has True and False but double-quoted keys. Will that still fail?
Yes. Capitalised booleans are Python, not JSON. JSON requires lowercase `true` and `false`. The converter handles this along with the quotes.
Related reading
Once your JSON parses, the next questions are usually about comparing or reshaping it. The JSON diff tool compares two documents structurally rather than line by line, so key reordering does not show up as a difference. For turning JSON into tabular data, the CSV and JSON converter goes both directions.