{"record":{"id":"737fdb02f1571889","repo":"microsoft/autogen","slug":"expected-json-object-but-found-language-languag","errorCode":null,"errorMessage":"Expected JSON object, but found language: {language}","messagePattern":"Expected JSON object, but found language: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-core/src/autogen_core/utils/_load_json.py","lineNumber":17,"sourceCode":"import json\nimport re\nfrom typing import Any, Dict, List\n\n\ndef extract_json_from_str(content: str) -> List[Dict[str, Any]]:\n    \"\"\"Extract JSON objects from a string. Supports backtick enclosed JSON objects\"\"\"\n    pattern = re.compile(r\"```(?:\\s*([\\w\\+\\-]+))?\\n([\\s\\S]*?)```\")\n    matches = pattern.findall(content)\n    ret: List[Dict[str, Any]] = []\n    # If no matches found, assume the entire content is a JSON object\n    if not matches:\n        ret.append(json.loads(content))\n    for match in matches:\n        language = match[0].strip() if match[0] else None\n        if language and language.lower() != \"json\":\n            raise ValueError(f\"Expected JSON object, but found language: {language}\")\n        content = match[1]\n        ret.append(json.loads(content))\n    return ret\n","sourceCodeStart":1,"sourceCodeEnd":21,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-core/src/autogen_core/utils/_load_json.py#L1-L21","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nobjs = extract_json_from_str(response)\n# after\nimport re\ncleaned = re.sub(r\"```(?!json)[\\w\\+\\-]*\", \"```\", response)\nobjs = extract_json_from_str(cleaned)","handlingStrategy":"validation","validationCode":"import re\nFENCE = re.compile(r\"```(?:\\s*([\\w\\+\\-]+))?\\n([\\s\\S]*?)```\")\ndef fences_are_json(content: str) -> bool:\n    return all((not m[0]) or m[0].strip().lower() == \"json\" for m in FENCE.findall(content))","typeGuard":null,"tryCatchPattern":"try:\n    objs = extract_json_from_str(text)\nexcept ValueError:\n    # strip or re-tag non-json fences, then retry once\n    text = re.sub(r\"```(?!json)[\\w\\+\\-]*\", \"```\", text)\n    objs = extract_json_from_str(text)","preventionTips":["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."],"tags":["json","parsing","llm-output","autogen-core"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}