{"record":{"id":"2e8f926d40d59964","repo":"infiniflow/ragflow","slug":"failed-to-build-bigquery-credentials-exc","errorCode":null,"errorMessage":"Failed to build BigQuery credentials: {exc}","messagePattern":"Failed to build BigQuery credentials: (.+?)","errorType":"validation","errorClass":"ConnectorValidationError","httpStatus":null,"severity":"error","filePath":"common/data_source/bigquery_connector.py","lineNumber":183,"sourceCode":"        self._credentials = {\"service_account_info\": service_account_info}\n        return None\n\n    def _get_client(self):\n        \"\"\"Create and cache a BigQuery client from the loaded service account.\"\"\"\n        if self._client is not None:\n            return self._client\n\n        if bigquery is None or service_account is None:\n            raise ConnectorValidationError(\"BigQuery client not installed. Please install google-cloud-bigquery.\")\n\n        service_account_info = self._credentials.get(\"service_account_info\")\n        if not service_account_info:\n            raise ConnectorMissingCredentialError(\"BigQuery credentials not loaded.\")\n\n        try:\n            creds = service_account.Credentials.from_service_account_info(service_account_info)\n        except Exception as exc:\n            raise ConnectorValidationError(f\"Failed to build BigQuery credentials: {exc}\")\n\n        try:\n            self._client = bigquery.Client(\n                project=self.project_id or None,\n                credentials=creds,\n                location=self.location or None,\n            )\n        except Exception as exc:\n            raise ConnectorValidationError(f\"Failed to create BigQuery client: {exc}\")\n\n        return self._client\n\n    # ------------------------------------------------------------------ #\n    # Query construction\n    # ------------------------------------------------------------------ #\n    def _build_base_query(self) -> str:\n        \"\"\"Return the single base query (custom query takes precedence over table mode).\"\"\"\n        if self.query:","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/bigquery_connector.py#L165-L201","documentation":"Raised when google.oauth2.service_account.Credentials.from_service_account_info throws while parsing the service account JSON — the JSON parsed fine (or was a dict) but is not a usable service account payload. Wrapped as ConnectorValidationError with the original exception chained.","triggerScenarios":"The parsed service_account_info is missing required fields for google-auth (typically 'token_uri' or 'private_key'), contains a placeholder/private_key_id without a real private key, or the private_key PEM block is malformed/truncated.","commonSituations":"Hand-rolled or templated service account JSON with only project_id/client_email, a key whose private_key newlines were flattened to literal '\\n' loss by a config system, or a workflow identity file mistaken for a service account key.","solutions":["Re-download the real key JSON from GCP IAM (it must include type, project_id, private_key_id, private_key, client_email, client_id, token_uri) — never hand-write it","If the key passes through env vars/YAML, ensure embedded newlines in private_key survive ('\\n' sequences intact)","Check exc.__cause__ — google-auth names the exact missing field","If you only have a workflow identity, use a different auth path; this connector requires a service account key"],"exampleFix":"# before: template with placeholder key\nsa = {\"project_id\": \"p\", \"client_email\": \"a@p.iam.gserviceaccount.com\", \"private_key\": \"REPLACE_ME\"}\n\n# after: use the actual downloaded key file contents\nimport json\nsa = json.load(open(\"/secure/path/sa-key.json\"))","handlingStrategy":"validation","validationCode":"REQUIRED_SA_FIELDS = {\"type\", \"project_id\", \"private_key_id\", \"private_key\", \"client_email\", \"client_id\", \"token_uri\"}\ndef validate_sa_info(sa: dict) -> None:\n    missing = REQUIRED_SA_FIELDS - sa.keys()\n    if missing:\n        raise ValueError(f\"service account JSON missing fields: {sorted(missing)}\")\n    if \"-----BEGIN PRIVATE KEY-----\" not in sa[\"private_key\"]:\n        raise ValueError(\"private_key is malformed or a placeholder\")","typeGuard":"def looks_like_service_account(sa: dict) -> bool:\n    return (\n        isinstance(sa, dict)\n        and sa.get(\"type\") == \"service_account\"\n        and \"private_key\" in sa\n        and \"client_email\" in sa\n        and \"token_uri\" in sa\n    )","tryCatchPattern":"try:\n    connector.validate_connector_settings()\nexcept ConnectorValidationError as e:\n    if \"Failed to build BigQuery credentials\" in str(e):\n        raise ConfigError(\"service account JSON is not a real GCP key file\") from e\n    raise","preventionTips":["Never hand-write service account JSON; always download from GCP IAM","Validate required fields and the private_key PEM header before passing to the connector","Preserve embedded newlines in private_key through every config layer (use base64 if a layer mangles them)"],"tags":["bigquery","gcp","service-account","credentials","validation"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}