{"record":{"id":"61ef05a7daa03c78","repo":"HumanSignal/label-studio","slug":"google-application-credentials-must-be-valid-json","errorCode":null,"errorMessage":"Google Application Credentials must be valid JSON string. {e}","messagePattern":"Google Application Credentials must be valid JSON string\\. (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"label_studio/io_storages/gcs/utils.py","lineNumber":71,"sourceCode":"        cls, google_project_id: str = None, google_application_credentials: Union[str, dict] = None\n    ) -> gcs.Client:\n        \"\"\"\n        :param google_project_id:\n        :param google_application_credentials:\n        :return:\n        \"\"\"\n        google_project_id = google_project_id or GCS.DEFAULT_GOOGLE_PROJECT_ID\n        cache_key = google_application_credentials\n\n        if cache_key not in GCS._client_cache:\n            # use credentials from LS Cloud Storage settings\n            if google_application_credentials:\n                if isinstance(google_application_credentials, str):\n                    try:\n                        google_application_credentials = json.loads(google_application_credentials)\n                    except JSONDecodeError as e:\n                        # change JSON error to human-readable format\n                        raise ValueError(f'Google Application Credentials must be valid JSON string. {e}')\n                credentials = service_account.Credentials.from_service_account_info(google_application_credentials)\n                GCS._client_cache[cache_key] = gcs.Client(project=google_project_id, credentials=credentials)\n\n            # use Google Application Default Credentials (ADC)\n            else:\n                GCS._client_cache[cache_key] = gcs.Client(project=google_project_id)\n\n        return GCS._client_cache[cache_key]\n\n    @classmethod\n    def validate_connection(\n        cls,\n        bucket_name: str,\n        google_project_id: str = None,\n        google_application_credentials: Union[str, dict] = None,\n        prefix: str = None,\n        use_glob_syntax: bool = False,\n    ):","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/gcs/utils.py#L53-L89","documentation":"get_client caches and builds a google-cloud-storage Client. When google_application_credentials is a string it must be the parsed JSON of a service account; a JSONDecodeError is re-raised as a ValueError telling the user the credentials must be a valid JSON string. Raised for any GCS operation: get_bucket, validate_connection, generate_http_url, get_blob_metadata, validate_pattern.","triggerScenarios":"Setting GOOGLE_APPLICATION_CREDENTIALS (or the storage field) to a file path instead of file contents, to a base64 blob, or to a truncated/corrupted JSON string; environment variable interpolation mangling newlines in the private key.","commonSituations":"Users copying the credentials file path into the UI field expecting Label Studio to read it; Docker/K8s secret mounted as a path then referenced directly; CI systems that base64-encode secrets; quotes/escaping lost when pasting into the settings form.","solutions":["Paste/read the actual JSON contents: with open('key.json') as f: creds = f.read(), then pass that string — not the path","If the secret is base64, decode it first: base64 -d key.b64 > key.json and use its contents","Validate the JSON before saving: python -m json.tool key.json; fix truncation/escaping (private_key newlines must survive as \\n inside the JSON)","Alternatively unset the credentials field and rely on Application Default Credentials (ADC) on a GCE/GKE instance with a proper service account"],"exampleFix":"// before\nGOOGLE_APPLICATION_CREDENTIALS=/secrets/key.json  // path -> JSONDecodeError\n// after\nexport GOOGLE_APPLICATION_CREDENTIALS=$(cat /secrets/key.json)  # raw JSON string\n# or leave unset and use ADC\n","handlingStrategy":"validation","validationCode":"import json\ndef credentials_field_ok(creds):\n    if not creds:\n        return True, 'using ADC'\n    try:\n        json.loads(creds)\n        return True, None\n    except json.JSONDecodeError as e:\n        return False, str(e)","typeGuard":"def is_service_account_json(s):\n    import json\n    try:\n        d = json.loads(s) if isinstance(s, str) else s\n        return isinstance(d, dict) and d.get('type') == 'service_account' and 'private_key' in d\n    except (json.JSONDecodeError, TypeError):\n        return False","tryCatchPattern":"try:\n    client = GCS.get_client(cache_key, google_project_id, google_application_credentials)\nexcept ValueError as e:\n    if 'valid JSON string' in str(e):\n        logger.error('Read the file contents: creds = open(path).read() — not the path itself')","preventionTips":["Pass the file contents (open(path).read()), never a path string","Validate with json.loads before setting the env var/UI field","Decode base64 CI secrets before use","Prefer ADC on GCE/GKE instead of embedding key JSON"],"tags":["gcs","google-cloud","credentials","configuration"],"backgroundTag":"invalid-credentials-json","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}