{"record":{"id":"74a5903b7c0d79ce","repo":"crewAIInc/crewAI","slug":"expected-an-async-readable-object-with-async-read","errorCode":null,"errorMessage":"Expected an async readable object with async read() method","messagePattern":"Expected an async readable object with async read\\(\\) method","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-files/src/crewai_files/core/sources.py","lineNumber":57,"sourceCode":"class _AsyncReadableValidator:\n    \"\"\"Pydantic validator for AsyncReadable types.\"\"\"\n\n    @classmethod\n    def __get_pydantic_core_schema__(\n        cls, _source_type: Any, _handler: GetCoreSchemaHandler\n    ) -> CoreSchema:\n        return core_schema.no_info_plain_validator_function(\n            cls._validate,\n            serialization=core_schema.plain_serializer_function_ser_schema(\n                lambda x: None, info_arg=False\n            ),\n        )\n\n    @staticmethod\n    def _validate(value: Any) -> AsyncReadable:\n        if isinstance(value, AsyncReadable):\n            return value\n        raise ValueError(\"Expected an async readable object with async read() method\")\n\n\nValidatedAsyncReadable = Annotated[AsyncReadable, _AsyncReadableValidator()]\n\n\ndef _detect_content_type_from_bytes(data: bytes) -> str | None:\n    if data.startswith(b\"\\x89PNG\\r\\n\\x1a\\n\"):\n        return \"image/png\"\n    if data.startswith(b\"\\xff\\xd8\\xff\"):\n        return \"image/jpeg\"\n    if data.startswith(b\"%PDF-\"):\n        return \"application/pdf\"\n\n    try:\n        decoded = data.decode(\"utf-8\")\n    except UnicodeDecodeError:\n        return None\n","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-files/src/crewai_files/core/sources.py#L39-L75","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap your object so it explicitly satisfies AsyncReadable (e.g. pass it through AsyncFileStream or implement the AsyncReadable protocol explicitly on your class).","If the data is already materialized, pass bytes via FileBytes instead of an async stream.","If it is a sync file-like object, use FileStream (which duck-types on read/seek)."],"exampleFix":"# before\nasync with aiofiles.open(\"x.pdf\", \"rb\") as f:\n    src = AsyncFileStream(stream=f)  # aiofiles handle is not AsyncReadable instance\n\n# after\ndata = await f.read()\nsrc = FileBytes(data=data)","handlingStrategy":"type-guard","validationCode":"from crewai_files.core.sources import AsyncReadable\n\ndef is_async_readable(v) -> bool:\n    return isinstance(v, AsyncReadable)","typeGuard":"from crewai_files.core.sources import AsyncReadable, AsyncFileStream\n\ndef as_async_source(v):\n    if isinstance(v, AsyncReadable):\n        return AsyncFileStream(stream=v)\n    if hasattr(v, \"read\") and hasattr(v, \"seek\"):\n        return v  # sync path; use FileStream\n    raise TypeError(\"unsupported source\")","tryCatchPattern":"try:\n    AsyncFileStream(stream=obj)\nexcept ValidationError as e:\n    if \"async readable\" in str(e):\n        data = await obj.read() if hasattr(obj.read, \"__await__\") else None  # fall back per API shape","preventionTips":["Prefer materialized bytes (FileBytes) unless you truly need streaming.","Remember this validator is isinstance-strict, not duck-typed."],"tags":["pydantic","validation","async","file-sources"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}