{"record":{"id":"a98d9ae102fd223b","repo":"risingwavelabs/risingwave","slug":"gcs-service-account-is-required-with-google-cloud","errorCode":null,"errorMessage":"gcs.service.account is required with Google Cloud Storage (GCS)","messagePattern":"gcs\\.service\\.account is required with Google Cloud Storage \\(GCS\\)","errorType":"validation","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/deltalake.rs","lineNumber":116,"sourceCode":"    pub async fn create_deltalake_client(&self) -> Result<DeltaTable> {\n        let table = match Self::get_table_url(&self.location)? {\n            DeltaTableUrl::S3(s3_path) => {\n                let storage_options = self.build_delta_lake_config_for_aws().await?;\n                deltalake::aws::register_handlers(None);\n                let url = Url::parse(&s3_path).map_err(|e| SinkError::DeltaLake(anyhow!(e)))?;\n                deltalake::open_table_with_storage_options(url, storage_options).await?\n            }\n            DeltaTableUrl::Local(local_path) => {\n                let url = Url::parse(&format!(\"file://{}\", local_path))\n                    .map_err(|e| SinkError::DeltaLake(anyhow!(e)))?;\n                deltalake::open_table(url).await?\n            }\n            DeltaTableUrl::Gcs(gcs_path) => {\n                let mut storage_options = HashMap::new();\n                storage_options.insert(\n                    GCS_SERVICE_ACCOUNT.to_owned(),\n                    self.gcs_service_account.clone().ok_or_else(|| {\n                        SinkError::Config(anyhow!(\n                            \"gcs.service.account is required with Google Cloud Storage (GCS)\"\n                        ))\n                    })?,\n                );\n                deltalake::gcp::register_handlers(None);\n                let url = Url::parse(&gcs_path).map_err(|e| SinkError::DeltaLake(anyhow!(e)))?;\n                deltalake::open_table_with_storage_options(url, storage_options).await?\n            }\n        };\n        Ok(table)\n    }\n\n    fn get_table_url(path: &str) -> Result<DeltaTableUrl> {\n        if path.starts_with(\"s3://\") || path.starts_with(\"s3a://\") {\n            Ok(DeltaTableUrl::S3(path.to_owned()))\n        } else if path.starts_with(\"gs://\") {\n            Ok(DeltaTableUrl::Gcs(path.to_owned()))\n        } else if let Some(path) = path.strip_prefix(\"file://\") {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/deltalake.rs#L98-L134","documentation":"To open a GCS-backed DeltaLake table, the connector requires the gcs.service.account option (a service account key). If DeltaTableUrl::Gcs was detected but gcs_service_account is None, it raises SinkError::Config because the GCS handler cannot authenticate without credentials.","triggerScenarios":"create_deltalake_client with a location starting with gs:// while the sink definition omits the 'gcs.service.account' option (it was not provided or was null).","commonSituations":"Creating a deltalake sink pointing at gs:// but forgetting the service account JSON; providing credentials under a wrong option name; assuming default credential chains work when the connector requires the explicit key.","solutions":["Add the option: gcs.service.account = '<service account JSON>' to the sink's deltalake options.","Verify the option key spelling is exactly 'gcs.service.account'.","Ensure the provided service account JSON is valid and has access to the bucket."],"exampleFix":"// before\nCREATE SINK s INTO DELTALAKE LOCATION = 'gs://bucket/table';\n// after\nCREATE SINK s INTO DELTALAKE LOCATION = 'gs://bucket/table'\n  WITH (connector = 'deltalake', gcs.service.account = '{\"type\":\"service_account\",...}');","handlingStrategy":"validation","validationCode":"if location.starts_with(\"gs://\") && !options.contains_key(\"gcs.service.account\") {\n    return Err(\"gcs.service.account is required for gs:// deltalake locations\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include gcs.service.account with the full service account JSON for gs:// locations.","Validate the JSON parses before creating the sink."],"tags":["rust","deltalake","gcs","config","credentials"],"backgroundTag":"missing-credentials","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}