microsoft/markitdown · error · TypeError

Invalid source type: {type(source)}. Expected str, requests.

Error message

Invalid source type: {type(source)}. Expected str, requests.Response, BinaryIO.

What it means

MarkItDown.convert() dispatches on the type of 'source': str/Path goes to convert_local, requests.Response to convert_response, and objects with a callable read() that are not io.TextIOBase to convert_stream. Anything else (bytes, int, an httpx.Response, a text-mode file, a tempfs handle without read) hits the final TypeError. The message names the received type so you can immediately see which branch you missed.

Source

Thrown at packages/markitdown/src/markitdown/_markitdown.py:321

                return self.convert_uri(source, stream_info=stream_info, **_kwargs)
            else:
                return self.convert_local(source, stream_info=stream_info, **kwargs)
        # Path object
        elif isinstance(source, Path):
            return self.convert_local(source, stream_info=stream_info, **kwargs)
        # Request response
        elif isinstance(source, requests.Response):
            return self.convert_response(source, stream_info=stream_info, **kwargs)
        # Binary stream
        elif (
            hasattr(source, "read")
            and callable(source.read)
            and not isinstance(source, io.TextIOBase)
        ):
            return self.convert_stream(source, stream_info=stream_info, **kwargs)
        else:
            raise TypeError(
                f"Invalid source type: {type(source)}. Expected str, requests.Response, BinaryIO."
            )

    def convert_local(
        self,
        path: Union[str, Path],
        *,
        stream_info: Optional[StreamInfo] = None,
        file_extension: Optional[str] = None,  # Deprecated -- use stream_info
        url: Optional[str] = None,  # Deprecated -- use stream_info
        **kwargs: Any,
    ) -> DocumentConverterResult:
        if isinstance(path, Path):
            path = str(path)

        # Build a base StreamInfo object from which to start guesses
        base_guess = StreamInfo(
            local_path=path,

View on GitHub (pinned to fd239d5d2b)

Solutions

  1. Wrap raw bytes: md.convert(io.BytesIO(data), stream_info=StreamInfo(extension='.pdf', mimetype='application/pdf'))
  2. For other HTTP clients, pass the body: md.convert(io.BytesIO(resp.content)) or construct a requests.Response
  3. Open files in binary mode: open(path, 'rb') or just pass the path string/Path
  4. For text-mode handles, reopen in 'rb'

Example fix

# before
with open("a.pdf", "r") as f:
    md.convert(f)  # TypeError: TextIOBase rejected
md.convert(resp.content)  # TypeError: bytes rejected

# after
with open("a.pdf", "rb") as f:
    md.convert(f)
md.convert(io.BytesIO(resp.content), stream_info=StreamInfo(extension=".pdf", mimetype="application/pdf"))
Defensive patterns

Strategy: type-guard

Validate before calling

import io, requests
from pathlib import Path

def is_valid_source(s) -> bool:
    return isinstance(s, (str, Path)) or isinstance(s, requests.Response) or (
        hasattr(s, "read") and callable(s.read) and not isinstance(s, io.TextIOBase)
    )

assert is_valid_source(source)

Type guard

from __future__ import annotations
import io, requests
from pathlib import Path
from typing import Union

ValidSource = Union[str, Path, requests.Response, io.BufferedIOBase]

def is_valid_markitdown_source(source: object) -> TypeGuard[ValidSource]:
    if isinstance(source, (str, Path)):
        return True
    if isinstance(source, requests.Response):
        return True
    return (
        hasattr(source, "read")
        and callable(source.read)
        and not isinstance(source, io.TextIOBase)
    )

Try / catch

try:
    result = md.convert(source)
except TypeError as e:
    if "Invalid source type" in str(e):
        source = io.BytesIO(source if isinstance(source, bytes) else bytes(source))
        result = md.convert(source, stream_info=StreamInfo(extension=ext, mimetype=mime))
    else:
        raise

Prevention

When it happens

Trigger: Calling md.convert() with raw bytes instead of a BytesIO, an httpx.Response instead of requests.Response, a file opened in text mode ('r'), or an arbitrary object like a dict or Path-like custom class that is not pathlib.Path.

Common situations: Mixing HTTP libraries (using httpx or aiohttp responses), reading a file with open(path, 'r') before passing it, or assuming convert() accepts byte content directly.

Related errors


AI-assisted analysis of microsoft/markitdown@fd239d5d2b (2026-08-14). Data as JSON: /api/errors/9870fbc680d42878. Report an issue: GitHub.