crewAIInc/crewAI · error · ValueError

Expected an async readable object with async read() method

Error message

Expected an async readable object with async read() method

What it means

Pydantic validator error from the AsyncReadable schema: a value passed where an async-readable source is expected is not an instance of AsyncReadable (the protocol exposing an async read() method). The validator is a strict isinstance check — duck-typed objects that merely define async read() are rejected, unlike the sync stream validator. Raised as ValueError during model validation, so it typically surfaces wrapped in pydantic.ValidationError.

Source

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

class _AsyncReadableValidator:
    """Pydantic validator for AsyncReadable 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) -> AsyncReadable:
        if isinstance(value, AsyncReadable):
            return value
        raise ValueError("Expected an async readable object with async read() method")


ValidatedAsyncReadable = Annotated[AsyncReadable, _AsyncReadableValidator()]


def _detect_content_type_from_bytes(data: bytes) -> str | None:
    if data.startswith(b"\x89PNG\r\n\x1a\n"):
        return "image/png"
    if data.startswith(b"\xff\xd8\xff"):
        return "image/jpeg"
    if data.startswith(b"%PDF-"):
        return "application/pdf"

    try:
        decoded = data.decode("utf-8")
    except UnicodeDecodeError:
        return None

View on GitHub (pinned to 754d7323be)

Solutions

  1. Wrap your object so it explicitly satisfies AsyncReadable (e.g. pass it through AsyncFileStream or implement the AsyncReadable protocol explicitly on your class).
  2. If the data is already materialized, pass bytes via FileBytes instead of an async stream.
  3. If it is a sync file-like object, use FileStream (which duck-types on read/seek).

Example fix

# before
async with aiofiles.open("x.pdf", "rb") as f:
    src = AsyncFileStream(stream=f)  # aiofiles handle is not AsyncReadable instance

# after
data = await f.read()
src = FileBytes(data=data)
Defensive patterns

Strategy: type-guard

Validate before calling

from crewai_files.core.sources import AsyncReadable

def is_async_readable(v) -> bool:
    return isinstance(v, AsyncReadable)

Type guard

from crewai_files.core.sources import AsyncReadable, AsyncFileStream

def as_async_source(v):
    if isinstance(v, AsyncReadable):
        return AsyncFileStream(stream=v)
    if hasattr(v, "read") and hasattr(v, "seek"):
        return v  # sync path; use FileStream
    raise TypeError("unsupported source")

Try / catch

try:
    AsyncFileStream(stream=obj)
except ValidationError as e:
    if "async readable" in str(e):
        data = await obj.read() if hasattr(obj.read, "__await__") else None  # fall back per API shape

Prevention

When it happens

Trigger: Passing an aiofiles file object, an httpx response, or a custom async stream to a field typed ValidatedAsyncReadable (e.g. AsyncFileStream(stream=...)) — anything that is async-readable but not literally an AsyncReadable instance.

Common situations: Users naturally try aiofiles.open(...) results or any object with async read(); because the check is isinstance-based rather than structural, these all fail even though they quack correctly.

Related errors


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