{"record":{"id":"bf1ac1a6ebcad9d9","repo":"infiniflow/ragflow","slug":"cloudflare-r2","errorCode":null,"errorMessage":"Cloudflare R2","messagePattern":"Cloudflare R2","errorType":"validation","errorClass":"ConnectorMissingCredentialError","httpStatus":null,"severity":"critical","filePath":"common/data_source/blob_connector.py","lineNumber":86,"sourceCode":"        # Populated by list_keys() so a subsequent get_value(key) can find the\n        # raw S3 object metadata (LastModified, ETag, Key, Size) without a second\n        # head_object call. Lifetime is one list_keys() pass.\n        self._listing_cache: dict[str, dict[str, Any]] = {}\n        self._filename_counts: dict[str, int] = {}\n\n    def set_allow_images(self, allow_images: bool) -> None:\n        \"\"\"Set whether to process images\"\"\"\n        logging.info(f\"Setting allow_images to {allow_images}.\")\n        self._allow_images = allow_images\n\n    def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None:\n        \"\"\"Load credentials\"\"\"\n        logging.debug(f\"Loading credentials for {self.bucket_name} of type {self.bucket_type}\")\n\n        # Validate credentials\n        if self.bucket_type == BlobType.R2:\n            if not all(credentials.get(key) for key in [\"r2_access_key_id\", \"r2_secret_access_key\", \"account_id\"]):\n                raise ConnectorMissingCredentialError(\"Cloudflare R2\")\n\n        elif self.bucket_type == BlobType.S3:\n            authentication_method = credentials.get(\"authentication_method\", \"access_key\")\n\n            if authentication_method == \"access_key\":\n                if not all(credentials.get(key) for key in [\"aws_access_key_id\", \"aws_secret_access_key\"]):\n                    raise ConnectorMissingCredentialError(\"Amazon S3\")\n\n            elif authentication_method == \"iam_role\":\n                if not credentials.get(\"aws_role_arn\"):\n                    raise ConnectorMissingCredentialError(\"Amazon S3 IAM role ARN is required\")\n\n            elif authentication_method == \"assume_role\":\n                pass\n\n            else:\n                raise ConnectorMissingCredentialError(\"Unsupported S3 authentication method\")\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/blob_connector.py#L68-L104","documentation":"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').","triggerScenarios":"R2 connector config where any of the three keys is absent or empty during load_credentials.","commonSituations":"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.","solutions":["Generate an R2 API token in the Cloudflare dashboard and copy the Access Key ID, Secret Access Key, and the account ID","Supply all three in the credentials dict with exact key names","Re-run load_credentials to confirm it passes"],"exampleFix":"// before\ncreds = {\"r2_access_key_id\": \"x\", \"r2_secret_access_key\": \"y\"}\n\n// after\ncreds = {\"r2_access_key_id\": \"x\", \"r2_secret_access_key\": \"y\", \"account_id\": \"a1b2c3\"}","handlingStrategy":"validation","validationCode":"R2_KEYS = (\"r2_access_key_id\", \"r2_secret_access_key\", \"account_id\")\nif not all(creds.get(k) for k in R2_KEYS):\n    missing = [k for k in R2_KEYS if not creds.get(k)]\n    raise ValueError(f\"missing R2 credentials: {missing}\")","typeGuard":"def is_r2_cred_dict(x) -> bool:\n    \"\"\"True when x has all non-empty Cloudflare R2 credential fields.\"\"\"\n    return isinstance(x, dict) and all(\n        isinstance(x.get(k), str) and x[k].strip() for k in (\"r2_access_key_id\", \"r2_secret_access_key\", \"account_id\")\n    )","tryCatchPattern":"try:\n    blob.load_credentials(creds)\nexcept ConnectorMissingCredentialError as e:\n    if \"Cloudflare R2\" in str(e):\n        creds = regenerate_r2_token_in_dashboard_and_refill()\n        blob.load_credentials(creds)","preventionTips":["Copy all three values from the R2 'Manage API Tokens' page in one sitting","Persist account_id alongside the keys — it is required, not optional","Validate the credential dict shape before saving the connector"],"tags":["cloudflare-r2","credentials","configuration","blob-storage"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}