FoundationAgents/MetaGPT · error · JSONDecodeError
Expecting property name enclosed in double quotes
Error message
Expecting property name enclosed in double quotes
What it means
This comes from MetaGPT's custom JSON decoder (metagpt/utils/custom_decoder.py), a fork of the stdlib json scanner that additionally supports single-quoted/quote-delimited keys. After parsing an object's opening quote character, if the first non-whitespace token is not a closing brace or a quote, it raises JSONDecodeError('Expecting property name enclosed in double quotes').
Source
Thrown at metagpt/utils/custom_decoder.py:137
# Use a slice to prevent IndexError from being raised, the following
# check will raise a more specific ValueError if the string is empty
nextchar = s[end : end + 1]
# Normally we expect nextchar == '"'
if nextchar != '"' and nextchar != "'":
if nextchar in _ws:
end = _w(s, end).end()
nextchar = s[end : end + 1]
# Trivial empty object
if nextchar == "}":
if object_pairs_hook is not None:
result = object_pairs_hook(pairs)
return result, end + 1
pairs = {}
if object_hook is not None:
pairs = object_hook(pairs)
return pairs, end + 1
elif nextchar != '"':
raise JSONDecodeError("Expecting property name enclosed in double quotes", s, end)
end += 1
while True:
if end + 1 < len(s) and s[end] == nextchar and s[end + 1] == nextchar:
# Handle the case where the next two characters are the same as nextchar
key, end = scanstring(s, end + 2, strict, delimiter=nextchar * 3)
else:
# Handle the case where the next two characters are not the same as nextchar
key, end = scanstring(s, end, strict, delimiter=nextchar)
key = memo_get(key, key)
# To skip some function call overhead we optimize the fast paths where
# the JSON key separator is ": " or just ":".
if s[end : end + 1] != ":":
end = _w(s, end).end()
if s[end : end + 1] != ":":
raise JSONDecodeError("Expecting ':' delimiter", s, end)
end += 1
try:View on GitHub (pinned to 11cdf466d0)
Solutions
- Inspect the exact character at the reported error position (the exception carries lineno/colno).
- Quote all keys and remove trailing commas: '{"a": 1}' not '{a: 1,}'.
- Pre-clean LLM output: json5-style repair or a regex to quote bare keys before decoding.
- Use standard double quotes throughout and validate with python -m json.tool.
Example fix
# before
loads("{a: 1,}") # bare key + trailing comma -> JSONDecodeError
# after
loads('{"a": 1}') Defensive patterns
Strategy: try-catch
Validate before calling
import re
def quote_bare_keys(text: str) -> str:
# quote bare keys and drop trailing commas for decoder compatibility
text = re.sub(r"([{,]\s*)([A-Za-z_]\w*)(\s*:)", r'\1"\2"\3', text)
return re.sub(r",(\s*[}\]])", r"\1", text) Try / catch
from json import JSONDecodeError
try:
obj = loads(text)
except JSONDecodeError as e:
if e.msg.startswith("Expecting property name"):
obj = loads(quote_bare_keys(text)) # repair and retry once Prevention
- Require double-quoted keys and no trailing commas in LLM JSON output.
- Use the exception's lineno/colno to pinpoint the offending character.
- Remember the custom decoder accepts single-quoted keys but not bare keys or trailing commas.
When it happens
Trigger: Decoding '{a: 1}' (bare unquoted key); '{1: "x"}' (numeric key); '{,}' or '{:}' stray punctuation; '{"a": 1,}' trailing comma before } — nextchar is ',' which is neither '"' nor '}'.
Common situations: LLM emits JSON with unquoted keys or a trailing comma; the relaxed decoder handles single quotes but NOT bare keys or trailing commas, so 'python-repr-like' dictionaries fail; text extracted from markdown retains a stray character after '{'.
Related errors
- Expecting ':' delimiter
- Cannot find the answer phrase "{response}"
- Invalid python code
- Could not find content between [{tag}] and [/{tag}]
- Error while extracting and parsing the {data_type}: {e}
AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14).
Data as JSON: /api/errors/7bfcf55906e1f83e.
Report an issue: GitHub.