{"record":{"id":"940693f489e71ef0","repo":"zylon-ai/private-gpt","slug":"binary-block-must-contain-base64-data-before-uploa","errorCode":null,"errorMessage":"Binary block must contain base64 data before upload","messagePattern":"Binary block must contain base64 data before upload","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/tools/binary_block_decorators.py","lineNumber":101,"sourceCode":"    blob_visibility: BlobVisibilityMode,\n    raise_on_error: bool = False,\n) -> None:\n    \"\"\"Transform a media block IN PLACE based on visibility mode.\n\n    Modifies the block's source directly to preserve type and serialization.\n\n    Args:\n        block: The block to transform (must have source and optional filename attrs)\n        blob_visibility: The visibility mode (PUBLIC or PRIVATE)\n        raise_on_error: Whether to raise on errors or log and skip\n    \"\"\"\n    try:\n        # If source is already URI-backed, skip transformation.\n        if isinstance(block.source, URIBinarySource) and _is_url(block.source.url):\n            return\n\n        if not isinstance(block.source, Base64BinarySource):\n            raise ValueError(\"Binary block must contain base64 data before upload\")\n        if not block.source.data:\n            raise ValueError(\"Binary block must contain base64 data before upload\")\n\n        # Get S3 helper from injector\n        s3_helper = get_global_injector().get(S3Helper)\n\n        # Generate unique identifiers\n        mime_type = block.source.media_type or \"application/octet-stream\"\n        if block.filename is None:\n            block.filename = _generate_filename(mime_type)\n        object_name = str(uuid.uuid4())\n        bucket_name = settings().s3.temporary_bucket_name\n\n        # Convert base64 data to bytes\n        bytes_data = _extract_bytes_from_data(block.source.data)\n\n        # Upload to S3\n        s3_url = await asyncio.to_thread(","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/tools/binary_block_decorators.py#L83-L119","documentation":"Raised in the binary-block-to-S3 upload transform (binary_block_decorators.py) when the block's source is not (or does not contain) a Base64BinarySource with non-empty data. The transform's job is to move inline base64 payloads into a temporary S3 bucket and replace them with a URI source; it explicitly skips URIBinarySource blocks that are already URLs, and raises for everything else — e.g. local-path sources or empty base64 payloads.","triggerScenarios":"Calling the upload decorator on a block whose source is a LocalPathBinarySource/other source class, or a Base64BinarySource with data='' / data=None. The raise_on_error flag controls whether this raises or is logged and skipped.","commonSituations":"Upload pipeline run before base64 ingestion populated the block; blocks created from local file paths that were never converted to base64; upstream ingestion silently storing empty data; calling the transform twice where a previous pass consumed the data.","solutions":["Ensure the block went through base64 ingestion before the S3 upload transform.","Check block.source type at runtime; skip blocks that are already URI-backed or lack data.","If mixed pipelines are expected, invoke the transform with raise_on_error=False so non-base64 blocks are logged and skipped.","For empty Base64BinarySource.data: fix the producer that created the block with empty content."],"exampleFix":"# before\ntransform_block_for_upload(block, blob_visibility, raise_on_error=True)  # raises\n\n# after\nif isinstance(block.source, Base64BinarySource) and block.source.data:\n    transform_block_for_upload(block, blob_visibility, raise_on_error=True)\nelse:\n    logger.warning(\"Skipping non-base64 block during upload: %r\", block.source)","handlingStrategy":"type-guard","validationCode":"from private_gpt.components.tools.binary_block_decorators import _is_url  # or local equivalent\n\ndef block_is_uploadable(block) -> bool:\n    src = getattr(block, \"source\", None)\n    if isinstance(src, URIBinarySource) and _is_url(src.url):\n        return False  # already uploaded\n    return isinstance(src, Base64BinarySource) and bool(src.data)","typeGuard":"def has_base64_payload(block) -> bool:\n    src = getattr(block, \"source\", None)\n    return isinstance(src, Base64BinarySource) and bool(src.data)","tryCatchPattern":"try:\n    transform_block_for_upload(block, visibility, raise_on_error=True)\nexcept ValueError as e:\n    if \"must contain base64 data\" in str(e):\n        logger.warning(\"skipping non-base64 block: %r\", block.source)\n    else:\n        raise","preventionTips":["Run the upload transform only after base64 ingestion","Use raise_on_error=False for mixed-source pipelines","Guard with has_base64_payload before transforming"],"tags":["binary","s3","upload","data-validation","ingestion"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}