zylon-ai/private-gpt · error · ValueError

INVALID_REQUEST_ERROR

INVALID_REQUEST_ERROR

Error message

Failed to upload file to S3 for async ingestion

What it means

ValueError (mapped to INVALID_REQUEST_ERROR) raised in the async ingestion scheduler when upload_file_to_s3 returns a falsy URL after attempting to push binary ingest content to the configured temporary S3 bucket. The task is never dispatched to Celery — the request fails before scheduling.

Source

Thrown at private_gpt/components/ingestion/ingestion_scheduler.py:451

        if ingest_body.ingest_body.input:
            should_upload = (
                not isinstance(ingest_body.ingest_body.input, UriArtifact)
                or ingest_body.ingest_body.input.is_base64()
            )
            s3_helper = self._require_s3_helper()
            if should_upload and s3_helper.is_available():
                content = ingest_body.ingest_body.input.to_binary_content(
                    get_file_name(ingest_body.ingest_body.metadata)
                )
                object_name = str(uuid.uuid4())
                s3_url = s3_helper.upload_file_to_s3(
                    filename=content.filename,
                    bytes_data=content.data.read(),
                    bucket_name=config.s3.temporary_bucket_name,
                    object_name=object_name,
                )
                if not s3_url:
                    raise ValueError("Failed to upload file to S3 for async ingestion")
                ingest_body.ingest_body.metadata = {
                    **(ingest_body.ingest_body.metadata or {}),
                    "file_name": content.filename,
                }
                ingest_body.ingest_body.input = UriArtifact(value=s3_url)

        result = dispatch_task(
            task_name=PARSE_TASK_NAME,
            args=(ingest_body,),
            queue=config.scheduler.ingestion.celery_queue,
        )
        task_id = result.task_id
        if not isinstance(task_id, str):
            raise ValueError("ingest_async did not return a valid task_id")
        return task_id

    def ingest(self, ingest_body: IngestBody) -> IngestResponse:
        """Parse then store synchronously, blocking until both complete."""

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Verify config.s3 settings: endpoint URL, credentials, and temporary_bucket_name all correct and the bucket exists.
  2. Test the helper directly: s3_helper.upload_file_to_s3(...) on a small file and inspect why it returns None (check its internal error swallowing).
  3. Check IAM/MinIO policy grants s3:PutObject on the temporary bucket.
  4. If S3 is not intended for this deployment, disable the should_upload path so content is passed inline.
Defensive patterns

Strategy: try-catch

Validate before calling

if should_upload:
    if not s3_helper.is_available():
        raise RuntimeError('S3 helper reports unavailable')
    if not config.s3.temporary_bucket_name:
        raise RuntimeError('s3.temporary_bucket_name not configured')

Try / catch

try:
    task_id = scheduler.ingest_async(body)
except ValueError as e:
    if 'Failed to upload file to S3' in str(e):
        return api_error(503, 'Storage backend temporarily unavailable; retry shortly')

Prevention

When it happens

Trigger: Submitting an async ingest request with should_upload true and s3_helper.is_available() true, but the S3 upload silently fails (helper returns None/empty instead of raising) — e.g. wrong credentials, unreachable endpoint, missing temporary_bucket_name bucket.

Common situations: Misconfigured S3 credentials or endpoint in config.s3; the temporary bucket does not exist or is in another region; MinIO not running in local dev; IAM policy without s3:PutObject.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/6ad170758f25a45d. Report an issue: GitHub.