infiniflow/ragflow · critical · ConnectorMissingCredentialError

Cloudflare R2

Error message

Cloudflare R2

What it means

BlobConnector.load_credentials for bucket_type R2 requires all of r2_access_key_id, r2_secret_access_key, and account_id; missing any one raises ConnectorMissingCredentialError('Cloudflare R2').

Source

Thrown at common/data_source/blob_connector.py:86

        # Populated by list_keys() so a subsequent get_value(key) can find the
        # raw S3 object metadata (LastModified, ETag, Key, Size) without a second
        # head_object call. Lifetime is one list_keys() pass.
        self._listing_cache: dict[str, dict[str, Any]] = {}
        self._filename_counts: dict[str, int] = {}

    def set_allow_images(self, allow_images: bool) -> None:
        """Set whether to process images"""
        logging.info(f"Setting allow_images to {allow_images}.")
        self._allow_images = allow_images

    def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None:
        """Load credentials"""
        logging.debug(f"Loading credentials for {self.bucket_name} of type {self.bucket_type}")

        # Validate credentials
        if self.bucket_type == BlobType.R2:
            if not all(credentials.get(key) for key in ["r2_access_key_id", "r2_secret_access_key", "account_id"]):
                raise ConnectorMissingCredentialError("Cloudflare R2")

        elif self.bucket_type == BlobType.S3:
            authentication_method = credentials.get("authentication_method", "access_key")

            if authentication_method == "access_key":
                if not all(credentials.get(key) for key in ["aws_access_key_id", "aws_secret_access_key"]):
                    raise ConnectorMissingCredentialError("Amazon S3")

            elif authentication_method == "iam_role":
                if not credentials.get("aws_role_arn"):
                    raise ConnectorMissingCredentialError("Amazon S3 IAM role ARN is required")

            elif authentication_method == "assume_role":
                pass

            else:
                raise ConnectorMissingCredentialError("Unsupported S3 authentication method")

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Generate an R2 API token in the Cloudflare dashboard and copy the Access Key ID, Secret Access Key, and the account ID
  2. Supply all three in the credentials dict with exact key names
  3. Re-run load_credentials to confirm it passes

Example fix

// before
creds = {"r2_access_key_id": "x", "r2_secret_access_key": "y"}

// after
creds = {"r2_access_key_id": "x", "r2_secret_access_key": "y", "account_id": "a1b2c3"}
Defensive patterns

Strategy: validation

Validate before calling

R2_KEYS = ("r2_access_key_id", "r2_secret_access_key", "account_id")
if not all(creds.get(k) for k in R2_KEYS):
    missing = [k for k in R2_KEYS if not creds.get(k)]
    raise ValueError(f"missing R2 credentials: {missing}")

Type guard

def is_r2_cred_dict(x) -> bool:
    """True when x has all non-empty Cloudflare R2 credential fields."""
    return isinstance(x, dict) and all(
        isinstance(x.get(k), str) and x[k].strip() for k in ("r2_access_key_id", "r2_secret_access_key", "account_id")
    )

Try / catch

try:
    blob.load_credentials(creds)
except ConnectorMissingCredentialError as e:
    if "Cloudflare R2" in str(e):
        creds = regenerate_r2_token_in_dashboard_and_refill()
        blob.load_credentials(creds)

Prevention

When it happens

Trigger: R2 connector config where any of the three keys is absent or empty during load_credentials.

Common situations: Forgotten account_id (it is part of the S3 endpoint but also validated separately); token created in R2 console but keys not copied fully; key name typos.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/bf1ac1a6ebcad9d9. Report an issue: GitHub.