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
- 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).
- Strip or re-tag non-JSON fences before calling: re.sub(r'```(?!json)[\w+-]*', '```', content).
- Pre-filter matches: only pass through fences whose language is empty or 'json', skipping others instead of failing.
- 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
- Instruct the model to output JSON only, in a ```json fence or bare.
- Pre-clean model output: drop fences whose language tag isn't json before parsing.
- Treat fenced snippets as optional — skip non-json fences instead of failing the whole parse.
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
- Progress ledger should contain a single JSON object, but fou
- Invalid agent id: {agent_id}
- Invalid topic id: {topic_id}
- Invalid JSON response from server. Check if the MCP route is
- Invalid JSON response from server. Check if the MCP route is
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/737fdb02f1571889.
Report an issue: GitHub.