xtekky/gpt4free · error · ValueError

Response must be a dict or have headers

Error message

Response must be a dict or have headers

What it means

Thrown by g4f.image.copy_media() (copy_images.py) when the response argument is neither a dict (with 'data'/'mimeType' keys) nor an object with .headers (e.g. an aiohttp/requests response), and no explicit content_type was passed. The function must know the media's MIME type to pick an extension and filename; with none of the three sources available it aborts.

Source

Thrown at g4f/image/copy_images.py:83

    return str(int(timestamp)) + "_" + filename.split("_", maxsplit=1)[-1]


async def save_response_media(
    response,
    prompt: str,
    tags: list[str] = None,
    transcript: str = None,
    content_type: str = None,
) -> AsyncIterator:
    """Save media from response to local file and return a response object."""
    if isinstance(response, dict):
        content_type = response.get("mimeType", content_type or "audio/mpeg")
        transcript = response.get("transcript")
        response = response.get("data")
    elif hasattr(response, "headers"):
        content_type = response.headers.get("content-type", content_type)
    elif not content_type:
        raise ValueError("Response must be a dict or have headers")

    if isinstance(response, str):
        response = base64.b64decode(response)

    extension = MEDIA_TYPE_MAP.get(content_type)
    if extension is None:
        raise ValueError(f"Unsupported media type: {content_type}")

    filename = get_filename(tags, prompt, f".{extension}", prompt)
    if hasattr(response, "headers"):
        filename = update_filename(response, filename)
    target_path = os.path.join(get_media_dir(), filename)
    ensure_media_dir()

    with open(target_path, "wb") as f:
        if isinstance(response, bytes):
            f.write(response)
        else:

View on GitHub (pinned to 973504e177)

Solutions

  1. Pass the MIME type explicitly: copy_media(data, content_type='audio/mpeg', ...).
  2. Or pass the original response dict {'data': ..., 'mimeType': ...} if the provider returned one.
  3. Or pass the response object itself (with .headers) instead of pre-extracted bytes.

Example fix

// before
return_media = await copy_media(audio_bytes, prompt=prompt, tags=tags)

// after
return_media = await copy_media(audio_bytes, prompt=prompt, tags=tags, content_type="audio/mpeg")
Defensive patterns

Strategy: validation

Validate before calling

def copy_media_ready(response, content_type) -> bool:
    return (
        isinstance(response, dict)
        or hasattr(response, "headers")
        or content_type is not None
    )

Prevention

When it happens

Trigger: copy_media(b'...mp3 bytes...') with content_type=None; copy_media('base64str') without content_type (str responses are decoded but still need a type); passing a custom response wrapper that exposes .content but not .headers.

Common situations: Adapters that already extracted bytes/str from a provider response and forward only the payload; newer provider return types (raw bytes) passed to a helper written around dict/curl_cffi responses.

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/70e10ef2bced8fba. Report an issue: GitHub.