microsoft/autogen · error · ValueError

Expected JSON object, but found language: {language}

Error message

Expected JSON object, but found language: {language}

What it means

extract_json_from_str parses fenced code blocks (```...```) out of a model response and expects them to be JSON. If a fence declares a language tag other than 'json' (case-insensitive), it raises ValueError instead of trying to parse the block — the function assumes every fenced block in the input is meant to be JSON output.

Source

Thrown at python/packages/autogen-core/src/autogen_core/utils/_load_json.py:17

import json
import re
from typing import Any, Dict, List


def extract_json_from_str(content: str) -> List[Dict[str, Any]]:
    """Extract JSON objects from a string. Supports backtick enclosed JSON objects"""
    pattern = re.compile(r"```(?:\s*([\w\+\-]+))?\n([\s\S]*?)```")
    matches = pattern.findall(content)
    ret: List[Dict[str, Any]] = []
    # If no matches found, assume the entire content is a JSON object
    if not matches:
        ret.append(json.loads(content))
    for match in matches:
        language = match[0].strip() if match[0] else None
        if language and language.lower() != "json":
            raise ValueError(f"Expected JSON object, but found language: {language}")
        content = match[1]
        ret.append(json.loads(content))
    return ret

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Re-prompt/adjust instructions so the model emits only ```json fences (or no fences at all — the no-match path json.loads the whole string).
  2. Strip or re-tag non-JSON fences before calling: re.sub(r'```(?!json)[\w+-]*', '```', content).
  3. Pre-filter matches: only pass through fences whose language is empty or 'json', skipping others instead of failing.
  4. If you control the caller, iterate matches yourself with the same regex rather than using this helper.

Example fix

# before
objs = extract_json_from_str(response)
# after
import re
cleaned = re.sub(r"```(?!json)[\w\+\-]*", "```", response)
objs = extract_json_from_str(cleaned)
Defensive patterns

Strategy: validation

Validate before calling

import re
FENCE = re.compile(r"```(?:\s*([\w\+\-]+))?\n([\s\S]*?)```")
def fences_are_json(content: str) -> bool:
    return all((not m[0]) or m[0].strip().lower() == "json" for m in FENCE.findall(content))

Try / catch

try:
    objs = extract_json_from_str(text)
except ValueError:
    # strip or re-tag non-json fences, then retry once
    text = re.sub(r"```(?!json)[\w\+\-]*", "```", text)
    objs = extract_json_from_str(text)

Prevention

When it happens

Trigger: Passing an LLM response to extract_json_from_str where any fenced code block is tagged python, sql, jsonc, javascript, etc. Even one non-json fence among several matches triggers the error; the language comparison only lowercases, so 'JSONC' or 'json5' still fail.

Common situations: Model answers that include explanatory code snippets plus the actual JSON; prompts that ask for mixed markdown output; models that tag the JSON block 'json5' or add a language to a plain block.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/737fdb02f1571889. Report an issue: GitHub.