huggingface/smolagents · error · ValueError

JSON is invalid: you probably tried to provide multiple tool

Error message

JSON is invalid: you probably tried to provide multiple tool calls in one action. PROVIDE ONLY ONE TOOL CALL.

What it means

A special case of JSON parsing failure in parse_json_blob: json.loads failed at a position where the blob contains '},\n', i.e., two adjacent JSON objects. smolagents allows only ONE tool call per action, so this pattern is detected and reported with a targeted message.

Source

Thrown at src/smolagents/utils.py:179

    else:
        # For any other type, convert to string
        return str(obj)


def parse_json_blob(json_blob: str) -> tuple[dict[str, str], str]:
    "Extracts the JSON blob from the input and returns the JSON data and the rest of the input."
    try:
        first_accolade_index = json_blob.find("{")
        last_accolade_index = [a.start() for a in list(re.finditer("}", json_blob))][-1]
        json_str = json_blob[first_accolade_index : last_accolade_index + 1]
        json_data = json.loads(json_str, strict=False)
        return json_data, json_blob[:first_accolade_index]
    except IndexError:
        raise ValueError("The model output does not contain any JSON blob.")
    except json.JSONDecodeError as e:
        place = e.pos
        if json_blob[place - 1 : place + 2] == "},\n":
            raise ValueError(
                "JSON is invalid: you probably tried to provide multiple tool calls in one action. PROVIDE ONLY ONE TOOL CALL."
            )
        raise ValueError(
            f"The JSON blob you used is invalid due to the following error: {e}.\n"
            f"JSON blob was: {json_blob}, decoding failed on that specific part of the blob:\n"
            f"'{json_blob[place - 4 : place + 5]}'."
        )


def extract_code_from_text(text: str, code_block_tags: tuple[str, str]) -> str | None:
    """Extract code from the LLM's output."""
    pattern = rf"{code_block_tags[0]}(.*?){code_block_tags[1]}"
    matches = re.findall(pattern, text, re.DOTALL)
    if matches:
        return "\n\n".join(match.strip() for match in matches)
    return None

View on GitHub (pinned to 30bb116109)

Solutions

  1. Adjust the prompt/system template to demand exactly one tool call per step (the error text is designed to be fed back to the model)
  2. Retry the step — the agent loop resubmits with this error as feedback
  3. Use a model/template pairing known to comply with smolagents' one-call-per-step protocol

Example fix

# before (model output)
{"name": "search", "arguments": {...}},
{"name": "visit", "arguments": {...}}

# after (one call per step)
{"name": "search", "arguments": {...}}
Defensive patterns

Strategy: retry

Validate before calling

def single_action_only(text: str) -> bool:
    import re
    return not re.search(r'\}\s*,\s*\{', text)

Type guard

def is_single_json_object(text: str) -> bool:
    import json, re
    m = re.search(r'\{.*\}', text, re.DOTALL)
    if not m:
        return False
    try:
        obj = json.loads(m.group(0))
        return isinstance(obj, dict)
    except json.JSONDecodeError:
        return False

Try / catch

try:
    parse_json_blob(text)
except ValueError as e:
    if 'PROVIDE ONLY ONE TOOL CALL' in str(e):
        # return error to agent so model retries with one call
        raise

Prevention

When it happens

Trigger: Model output contains multiple JSON action objects separated by a comma and newline, e.g. '{"action": ...},\n{"action": ...}'; typically with models eager to parallelize tool calls.

Common situations: Strong models trying to batch several tool calls per step; prompts encouraging 'do everything at once'; few-shot examples accidentally showing multiple calls per action.

Related errors


AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28). Data as JSON: /api/errors/b38e4bc71e642961. Report an issue: GitHub.