crewAIInc/crewAI · error · ValueError

Expected a binary file-like object with read() and seek()

Error message

Expected a binary file-like object with read() and seek()

What it means

Pydantic validator error from the BinaryIO schema: a value passed where a binary file-like object is expected lacks a 'read' or 'seek' attribute. Unlike the async validator, this one IS duck-typed — any object with read() and seek() passes. So hitting it means the value has neither or only one of the two methods, e.g. a text-mode file (which has both, so more commonly: an httpx/starlette response stream with no seek), a generator, or raw bytes.

Source

Thrown at lib/crewai-files/src/crewai_files/core/sources.py:200

class _BinaryIOValidator:
    """Pydantic validator for BinaryIO types."""

    @classmethod
    def __get_pydantic_core_schema__(
        cls, _source_type: Any, _handler: GetCoreSchemaHandler
    ) -> CoreSchema:
        return core_schema.no_info_plain_validator_function(
            cls._validate,
            serialization=core_schema.plain_serializer_function_ser_schema(
                lambda x: None, info_arg=False
            ),
        )

    @staticmethod
    def _validate(value: Any) -> BinaryIO:
        if hasattr(value, "read") and hasattr(value, "seek"):
            return cast(BinaryIO, value)
        raise ValueError("Expected a binary file-like object with read() and seek()")


ValidatedBinaryIO = Annotated[BinaryIO, _BinaryIOValidator()]


class FilePath(BaseModel):
    """File loaded from a filesystem path."""

    path: Path = Field(description="Path to the file on the filesystem.")
    max_size_bytes: int = Field(
        default=DEFAULT_MAX_FILE_SIZE_BYTES,
        exclude=True,
        description="Maximum file size in bytes.",
    )
    _content: bytes | None = PrivateAttr(default=None)
    _content_type: str = PrivateAttr()

    @model_validator(mode="after")

View on GitHub (pinned to 754d7323be)

Solutions

  1. Buffer the data first: data = await response.read() then use FileBytes(data=data), or wrap in io.BytesIO which has both read() and seek().
  2. If wrapping a custom class, add seek() (and tell/read) or read the full payload eagerly.
  3. For files, open in binary mode: open(path, 'rb').

Example fix

# before
resp = await client.get(url)
fs = FileStream(stream=resp)  # httpx response: no seek()

# after
resp = await client.get(url)
fs = FileBytes(data=resp.content)  # or FileStream(stream=io.BytesIO(resp.content))
Defensive patterns

Strategy: type-guard

Validate before calling

def is_binary_filelike(v) -> bool:
    return hasattr(v, "read") and hasattr(v, "seek")

Type guard

import io

def as_binary(v) -> io.BytesIO:
    if isinstance(v, (bytes, bytearray)):
        return io.BytesIO(v)
    if hasattr(v, "read") and hasattr(v, "seek"):
        return v
    raise TypeError(f"{type(v).__name__} is not a binary file-like object")

Try / catch

try:
    FileStream(stream=obj)
except ValidationError as e:
    if "binary file-like" in str(e):
        FileStream(stream=io.BytesIO(obj.read()))  # only if read() exists

Prevention

When it happens

Trigger: Passing an httpx response .aread()-style stream, a websocket/generator payload, io.StringIO wrapped oddly, or a custom buffer without seek() to a field typed ValidatedBinaryIO / FileStream(stream=...).

Common situations: Streaming downloads handed straight to FileStream without buffering; network stream objects that only support sequential reads; text streams where the developer expected automatic transcoding.

Related errors


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