{"record":{"id":"0391501d34d81a0e","repo":"hiyouga/LlamaFactory","slug":"invalid-json-format-in-tool-description-str-con","errorCode":null,"errorMessage":"Invalid JSON format in tool description: {str([content])}.","messagePattern":"Invalid JSON format in tool description: (.+?)\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/data/formatter.py","lineNumber":155,"sourceCode":"            function_str = self.tool_utils.function_formatter(functions)\n            function_str = thought_content + function_str\n\n        return super().apply(content=function_str)\n\n\n@dataclass\nclass ToolFormatter(Formatter):\n    def __post_init__(self):\n        self.tool_utils = get_tool_utils(self.tool_format)\n\n    @override\n    def apply(self, **kwargs) -> SLOTS:\n        content = kwargs.pop(\"content\")\n        try:\n            tools = json.loads(content)\n            return [self.tool_utils.tool_formatter(tools) if len(tools) != 0 else \"\"]\n        except json.JSONDecodeError:\n            raise RuntimeError(f\"Invalid JSON format in tool description: {str([content])}.\")  # flat string\n\n    @override\n    def extract(self, content: str) -> str | list[\"FunctionCall\"]:\n        return self.tool_utils.tool_extractor(content)\n","sourceCodeStart":137,"sourceCodeEnd":160,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/data/formatter.py#L137-L160","documentation":"Raised by ToolFormatter.apply when the `tools` column of a sample fails json.loads. LlamaFactory expects the tools description of a sharegpt-style dataset to be a valid JSON list of tool schemas; the formatter then renders it with the model-specific tool_utils.tool_formatter. A flat string or any non-JSON text triggers this RuntimeError during preprocessing.","triggerScenarios":"Running SFT on a sharegpt dataset whose `tools` field is plain text, a JSON object instead of a list, truncated JSON, or JSON wrapped in markdown fences; happens at the first sample containing a non-empty tools field when a tool_format is configured.","commonSituations":"Hand-written tools descriptions in natural language; datasets exported from OpenAI-format conversations where tools were serialized with single quotes or extra commas; mixing tool schema styles across rows.","solutions":["Make every non-empty `tools` field a strict JSON array of tool schemas, e.g. [{\"name\": ..., \"description\": ..., \"parameters\": {...}}].","Validate the column offline: json.loads(tools) and isinstance(tools, list) for all rows; repair or drop failures.","Remove markdown fences and prose around the JSON; ensure double quotes and no trailing commas.","If a sample genuinely has no tools, use an empty string or empty list instead of a textual note."],"exampleFix":"# before\n\"tools\": \"[{'name': 'get_weather', 'parameters': {'city': 'str'}}]\"\n\n# after\n\"tools\": \"[{\\\"name\\\": \\\"get_weather\\\", \\\"parameters\\\": {\\\"type\\\": \\\"object\\\", \\\"properties\\\": {\\\"city\\\": {\\\"type\\\": \\\"string\\\"}}}}]\"","handlingStrategy":"validation","validationCode":"import json\n\ndef tools_column_ok(rows: list[dict]) -> list[int]:\n    bad = []\n    for i, r in enumerate(rows):\n        tools = r.get(\"tools\")\n        if tools:\n            try:\n                if not isinstance(json.loads(tools), list):\n                    bad.append(i)\n            except json.JSONDecodeError:\n                bad.append(i)\n    return bad","typeGuard":"def is_valid_tools_json(tools: str) -> bool:\n    try:\n        parsed = json.loads(tools)\n        return isinstance(parsed, list) and all(isinstance(t, dict) and \"name\" in t for t in parsed)\n    except (json.JSONDecodeError, TypeError):\n        return False","tryCatchPattern":"try:\n    json.loads(tools_field)\nexcept json.JSONDecodeError:\n    # rewrite row with \"\" (no tools) or drop it, and record the index\n    pass","preventionTips":["Generate the tools field programmatically with json.dumps rather than by hand.","Keep tool schemas in OpenAI function-calling shape (list of objects with name/description/parameters).","Add a CI lint step for dataset JSONL files."],"tags":["json","tools","data-format","sharegpt"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}