{"record":{"id":"c115473f50e76c92","repo":"infiniflow/ragflow","slug":"bigquery-service-account-json-is-not-valid-json","errorCode":null,"errorMessage":"BigQuery: service_account_json is not valid JSON: {exc}","messagePattern":"BigQuery: service_account_json is not valid JSON: (.+?)","errorType":"validation","errorClass":"ConnectorMissingCredentialError","httpStatus":null,"severity":"error","filePath":"common/data_source/bigquery_connector.py","lineNumber":159,"sourceCode":"    # ------------------------------------------------------------------ #\n    # Credentials & client\n    # ------------------------------------------------------------------ #\n    def load_credentials(self, credentials: Dict[str, Any]) -> Dict[str, Any] | None:\n        \"\"\"Load BigQuery service-account credentials.\n\n        Accepts ``service_account_json`` as either a dict or a JSON string.\n        \"\"\"\n        logging.debug(\"Loading credentials for BigQuery project: %s\", self.project_id)\n\n        raw = (credentials or {}).get(\"service_account_json\")\n        if not raw:\n            raise ConnectorMissingCredentialError(\"BigQuery: missing service_account_json\")\n\n        if isinstance(raw, str):\n            try:\n                service_account_info = json.loads(raw)\n            except json.JSONDecodeError as exc:\n                raise ConnectorMissingCredentialError(f\"BigQuery: service_account_json is not valid JSON: {exc}\")\n        elif isinstance(raw, dict):\n            service_account_info = raw\n        else:\n            raise ConnectorMissingCredentialError(\"BigQuery: service_account_json must be a JSON string or object\")\n\n        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:","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/bigquery_connector.py#L141-L177","documentation":"Raised when service_account_json is supplied as a string but json.loads fails — the string is not valid JSON. The JSONDecodeError detail is embedded, and the error type is ConnectorMissingCredentialError (credential material unusable, treated as a credential problem).","triggerScenarios":"Passing service_account_json as a string that is not parseable JSON: truncated key file, Python-repr dict string (\"{'type': ...}\" with single quotes), a value with a BOM or stray characters, or newlines introduced by env-var encoding.","commonSituations":"Pasting the key file into an env var with wrapping/truncation, double-encoding (the JSON string was serialized again), or using repr(dict) instead of json.dumps(dict) in glue code.","solutions":["Re-download the service account JSON key from GCP and pass its full contents verbatim","If building the string from a dict in code, use json.dumps(obj) — never str(obj)/repr(obj)","Check the embedded JSONDecodeError position to see where parsing broke (usually truncation)","If the value came through an env var, verify no shell quoting mangled it (print len() and first/last chars)"],"exampleFix":"# before\ncreds = {\"service_account_json\": str(sa_dict)}  # repr, not JSON\n\n# after\nimport json\ncreds = {\"service_account_json\": json.dumps(sa_dict)}\n# or simply pass the dict directly\ncreds = {\"service_account_json\": sa_dict}","handlingStrategy":"validation","validationCode":"import json\ndef normalize_sa_json(raw) -> dict:\n    if isinstance(raw, dict):\n        return raw\n    if isinstance(raw, str):\n        return json.loads(raw)  # raises JSONDecodeError here with a clear stack\n    raise TypeError(\"service_account_json must be str or dict\")","typeGuard":"def is_parseable_sa_json(raw) -> bool:\n    if isinstance(raw, dict):\n        return True\n    if isinstance(raw, str):\n        try:\n            json.loads(raw)\n            return True\n        except json.JSONDecodeError:\n            return False\n    return False","tryCatchPattern":"try:\n    connector.load_credentials(creds)\nexcept ConnectorMissingCredentialError as e:\n    if \"not valid JSON\" in str(e):\n        raise ConfigError(\"re-paste the full key file; JSON was truncated or repr'd\") from e\n    raise","preventionTips":["Never build the JSON string with str()/repr() — use json.dumps or pass the dict","Pass key files as files or parsed dicts end-to-end; avoid shell env vars for multi-line JSON","Include a length check: real key files are >1KB; anything much shorter is truncated"],"tags":["bigquery","gcp","json","credentials","parsing"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}