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

  1. Inspect the exact character at the reported error position (the exception carries lineno/colno).
  2. Quote all keys and remove trailing commas: '{"a": 1}' not '{a: 1,}'.
  3. Pre-clean LLM output: json5-style repair or a regex to quote bare keys before decoding.
  4. 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

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


AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14). Data as JSON: /api/errors/7bfcf55906e1f83e. Report an issue: GitHub.