crewAIInc/crewAI · error · ValueError

Unsupported data format: {data_format}. Must be one of {', '

Error message

Unsupported data format: {data_format}. Must be one of {', '.join(valid_data_formats)}.

What it means

Raised by BrightDataUnlockerTool._run when data_format (falling back to self.data_format) is not 'html' or 'markdown'. The value controls whether the payload requests raw HTML or a markdown rendering of the unlocked page; anything else — including None when no default was set at construction — is rejected before the request.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_unlocker.py:127

        format: str | None = None,
        data_format: str | None = None,
        **kwargs: Any,
    ) -> Any:
        url = url or self.url
        format = format or self.format
        data_format = data_format or self.data_format

        if not url:
            raise ValueError("url is required either in constructor or method call")

        payload = {
            "url": url,
            "zone": self.zone,
            "format": format,
        }
        valid_data_formats = {"html", "markdown"}
        if data_format not in valid_data_formats:
            raise ValueError(
                f"Unsupported data format: {data_format}. Must be one of {', '.join(valid_data_formats)}."
            )

        if data_format == "markdown":
            payload["data_format"] = "markdown"

        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }

        validate_url(url)
        try:
            response = requests.post(
                self.base_url, json=payload, headers=headers, timeout=30
            )
            response.raise_for_status()

View on GitHub (pinned to 754d7323be)

Solutions

  1. Use exactly 'html' or 'markdown' (lowercase).
  2. Set the default in the constructor: BrightDataUnlockerTool(url=..., data_format='markdown').
  3. Normalize agent-generated values to lowercase and whitelist before invoking.

Example fix

# before
tool.run(url='https://example.com', data_format='txt')  # ValueError

# after
tool.run(url='https://example.com', data_format='markdown')
Defensive patterns

Strategy: validation

Validate before calling

VALID_DATA_FORMATS = {"html", "markdown"}

data_format = (data_format or 'markdown').lower()
if data_format not in VALID_DATA_FORMATS:
    raise ValueError(f"data_format must be one of {sorted(VALID_DATA_FORMATS)}")
result = tool.run(url=url, data_format=data_format)

Type guard

def is_valid_data_format(v: object) -> bool:
    return isinstance(v, str) and v in {"html", "markdown"}

Try / catch

try:
    result = tool.run(url=url, data_format=data_format)
except ValueError as e:
    if "Unsupported data format" in str(e):
        result = tool.run(url=url, data_format='markdown')
    else:
        raise

Prevention

When it happens

Trigger: Calling tool.run(url=..., data_format='json'); passing data_format=None when the tool was constructed without a data_format argument; uppercase 'Markdown'.

Common situations: Confusing data_format with the dataset tool's format ('csv'/'json'), relying on a constructor default that isn't always present, case-sensitive inputs from LLM agents.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/b659ca8005cac5d2. Report an issue: GitHub.