{"record":{"id":"fce69bf8fd2d25b7","repo":"risingwavelabs/risingwave","slug":"failed-to-create-google-cloud-pub-sub-credentials","errorCode":null,"errorMessage":"Failed to create Google Cloud Pub/Sub credentials file","messagePattern":"Failed to create Google Cloud Pub/Sub credentials file","errorType":"error_code","errorClass":"SinkError::GooglePubSub","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/google_pubsub.rs","lineNumber":223,"sourceCode":"    add_future: DeliveryFutureManagerAddFuture<'w, GooglePubSubSinkDeliveryFuture>,\n}\n\nimpl GooglePubSubSinkWriter {\n    pub async fn new(\n        config: GooglePubSubConfig,\n        schema: Schema,\n        pk_indices: Vec<usize>,\n        format_desc: &SinkFormatDesc,\n        db_name: String,\n        sink_from_name: String,\n    ) -> Result<Self> {\n        let environment = if let Some(ref cred) = config.credentials {\n            let mut auth_config = project::Config::default();\n            auth_config = auth_config.with_audience(apiv1::conn_pool::AUDIENCE);\n            auth_config = auth_config.with_scopes(&apiv1::conn_pool::SCOPES);\n            let cred_file = CredentialsFile::new_from_str(cred).await.map_err(|e| {\n                SinkError::GooglePubSub(\n                    anyhow!(e).context(\"Failed to create Google Cloud Pub/Sub credentials file\"),\n                )\n            })?;\n            let provider =\n                DefaultTokenSourceProvider::new_with_credentials(auth_config, Box::new(cred_file))\n                    .await\n                    .map_err(|e| {\n                        SinkError::GooglePubSub(\n                            anyhow!(e).context(\n                                \"Failed to create Google Cloud Pub/Sub token source provider\",\n                            ),\n                        )\n                    })?;\n            Environment::GoogleCloud(Box::new(provider))\n        } else if let Some(emu_host) = config.emulator_host {\n            Environment::Emulator(emu_host)\n        } else {\n            return Err(SinkError::GooglePubSub(anyhow!(\n                \"Missing emulator_host or credentials in Google Pub/Sub sink\"","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/google_pubsub.rs#L205-L241","documentation":"When `credentials` are configured, the sink parses the service-account JSON string with gcloud-sdk's CredentialsFile::new_from_str. If the string is not valid credentials JSON (bad key fields, truncated copy, wrong value), the underlying error is wrapped with the context 'Failed to create Google Cloud Pub/Sub credentials file'.","triggerScenarios":"`pubsub.credentials` property contains malformed service-account JSON, e.g. copied with surrounding quotes/escapes, truncated, or a non-credential JSON object.","commonSituations":"Secrets managers injecting escaped/encoded JSON; pasting only part of a key file; using an API key instead of a service-account key JSON.","solutions":["Inspect the inner error chained under this message to see which credential field failed validation","Provide the full, unmodified service-account key JSON as the credentials value","Validate the JSON locally (e.g. `jq . key.json` and check fields like client_email/private_key) and re-create the sink"],"exampleFix":"// before\npubsub.credentials='{\"type\": \"service_account\", \"client_email\": ...'  -- truncated\n// after\npubsub.credentials='{\"type\":\"service_account\",\"project_id\":\"p\",\"private_key_id\":\"..\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\"client_email\":\"sa@p.iam.gserviceaccount.com\", ...}'","handlingStrategy":"validation","validationCode":"fn validate_credentials_json(cred: &str) -> Result<(), String> {\n    let v: serde_json::Value = serde_json::from_str(cred).map_err(|e| format!(\"credentials is not valid JSON: {e}\"))?;\n    for field in [\"type\", \"private_key\", \"client_email\"] {\n        if v.get(field).is_none() { return Err(format!(\"credentials missing field: {field}\")); }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Parse the credentials JSON with jq locally before wiring it in","Copy key files without truncation or shell escaping","Use raw/verbatim secret injection rather than hand-edited strings"],"tags":["pubsub","gcp","credentials"],"backgroundTag":"json-parse-error","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"}