{"record":{"id":"20c1952cc7ed750c","repo":"infiniflow/ragflow","slug":"credential-issue-encountered-error-code","errorCode":null,"errorMessage":"Credential issue encountered ({error_code}).","messagePattern":"Credential issue encountered \\((.+?)\\)\\.","errorType":"validation","errorClass":"CredentialExpiredError","httpStatus":null,"severity":"error","filePath":"common/data_source/blob_connector.py","lineNumber":337,"sourceCode":"            # Lightweight validation step\n            self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=self.prefix, MaxKeys=1)\n\n        except Exception as e:\n            error_code = getattr(e, \"response\", {}).get(\"Error\", {}).get(\"Code\", \"\")\n            status_code = getattr(e, \"response\", {}).get(\"ResponseMetadata\", {}).get(\"HTTPStatusCode\")\n\n            # Common S3 error scenarios\n            if error_code in [\n                \"AccessDenied\",\n                \"InvalidAccessKeyId\",\n                \"SignatureDoesNotMatch\",\n            ]:\n                if status_code == 403 or error_code == \"AccessDenied\":\n                    raise InsufficientPermissionsError(f\"Insufficient permissions to list objects in bucket '{self.bucket_name}'. Please check your bucket policy and/or IAM policy.\")\n                if status_code == 401 or error_code == \"SignatureDoesNotMatch\":\n                    raise CredentialExpiredError(\"Provided blob storage credentials appear invalid or expired.\")\n\n                raise CredentialExpiredError(f\"Credential issue encountered ({error_code}).\")\n\n            if error_code == \"NoSuchBucket\" or status_code == 404:\n                raise ConnectorValidationError(f\"Bucket '{self.bucket_name}' does not exist or cannot be found.\")\n\n            raise ConnectorValidationError(f\"Unexpected S3 client error (code={error_code}, status={status_code}): {e}\")\n\n\nif __name__ == \"__main__\":\n    # Example usage\n    credentials_dict = {\n        \"aws_access_key_id\": os.environ.get(\"AWS_ACCESS_KEY_ID\"),\n        \"aws_secret_access_key\": os.environ.get(\"AWS_SECRET_ACCESS_KEY\"),\n    }\n\n    # Initialize connector\n    connector = BlobStorageConnector(\n        bucket_type=os.environ.get(\"BUCKET_TYPE\") or \"s3\",\n        bucket_name=os.environ.get(\"BUCKET_NAME\") or \"yyboombucket\",","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/blob_connector.py#L319-L355","documentation":"Raised by BlobStorageConnector.validate_connector_settings as the fallback credential branch: the probe failed with one of AccessDenied / InvalidAccessKeyId / SignatureDoesNotMatch, but neither the 403/AccessDenied condition (insufficient permissions) nor the 401/SignatureDoesNotMatch condition (invalid credentials) matched. The original S3 error code is embedded in the CredentialExpiredError message.","triggerScenarios":"InvalidAccessKeyId with a non-403 status, or SignatureDoesNotMatch with a non-401 status — typical of S3-compatible providers (MinIO, Ceph, R2, OCI) that return nonstandard HTTP status codes with these AWS-style error codes. The connector conservatively assumes a credential problem.","commonSituations":"MinIO returning 403 SignatureDoesNotMatch when the secret is wrong (but the first branch catches AccessDenied-named codes at 403, so mismatched combinations fall through); clock skew breaking request signing against some providers; providers that map expired keys to odd statuses.","solutions":["Re-verify the credential pair for correctness (key id exists, secret matches) even though the message is generic — read the embedded error_code","If the provider is S3-compatible, test the same credentials with its own CLI (mc, rclone) to isolate status-code quirks","Check system clock sync (NTP) — signing uses the timestamp, and skew can produce signature errors with unusual statuses","Regenerate the key pair on the provider and update the connector config if the key is expired or revoked"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"# isolate the cause before the connector does its opaque mapping\ntry:\n    s3.list_objects_v2(Bucket=bucket, MaxKeys=1)\nexcept botocore.exceptions.ClientError as e:\n    code = e.response.get('Error', {}).get('Code')\n    status = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')\n    log.warning('probe failed code=%s status=%s', code, status)","typeGuard":null,"tryCatchPattern":"from common.data_source.exceptions import CredentialExpiredError, ConnectorValidationError\ntry:\n    connector.validate_connector_settings()\nexcept CredentialExpiredError as e:\n    code = parse_embedded_code(e)  # original S3 error code is in the message\n    if code == 'InvalidAccessKeyId':\n        rotate_credentials()\n    else:\n        raise","preventionTips":["Sync host clocks via NTP — signing skew produces odd status/code combinations that land in this branch","For S3-compatible providers, smoke-test credentials with the provider CLI to learn its status-code quirks before wiring the connector"],"tags":["credentials","s3-compatible","error-mapping","validation"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}