{"record":{"id":"1f25db20ab90b566","repo":"risingwavelabs/risingwave","slug":"missing-emulator-host-or-credentials-in-google-pub","errorCode":null,"errorMessage":"Missing emulator_host or credentials in Google Pub/Sub sink","messagePattern":"Missing emulator_host or credentials in Google Pub/Sub sink","errorType":"validation","errorClass":"SinkError::GooglePubSub","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/google_pubsub.rs","lineNumber":240,"sourceCode":"                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\"\n            )));\n        };\n\n        let client_config = ClientConfig {\n            endpoint: config.endpoint,\n            project_id: Some(config.project_id),\n            environment,\n            ..Default::default()\n        };\n        let client = Client::new(client_config)\n            .await\n            .map_err(|e| SinkError::GooglePubSub(anyhow!(e)))?;\n\n        let topic = async {\n            let topic = client.topic(&config.topic);\n            if !topic.exists(None).await? {\n                topic.create(None, None).await?;","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/google_pubsub.rs#L222-L258","documentation":"This is a defensive final branch in sink construction: after handling credentials and emulator_host, neither is set, so the environment cannot be built. Normally validate() catches this earlier with a clearer message; this error surfaces when the sink is constructed without that validation having run (or config was mutated in between).","triggerScenarios":"Building GooglePubSubSink::new directly with a config lacking both `credentials` and `emulator_host`, bypassing the validate() path.","commonSituations":"Programmatic sink construction in tests/tools; config built from a properties map that dropped both keys.","solutions":["Set `pubsub.credentials` or `pubsub.emulator_host` in the sink properties","Run validate() on the sink before new() so the earlier, clearer message catches the misconfiguration","Double-check the properties map passed to GooglePubSubConfig::from_btreemap contains the intended keys"],"exampleFix":"// before\nlet config = GooglePubSubConfig { emulator_host: None, credentials: None, .. };\n// after\nlet config = GooglePubSubConfig { emulator_host: Some(\"localhost:8085\".into()), credentials: None, .. };","handlingStrategy":"validation","validationCode":"fn ensure_env(config: &GooglePubSubConfig) -> Result<(), String> {\n    if config.emulator_host.is_none() && config.credentials.is_none() {\n        return Err(\"missing emulator_host or credentials\".into());\n    }\n    Ok(())\n}","typeGuard":"fn has_pubsub_env(c: &GooglePubSubConfig) -> bool {\n    c.emulator_host.is_some() || c.credentials.is_some()\n}","tryCatchPattern":"match sink.validate().await {\n    Err(e) => eprintln!(\"sink misconfigured: {e:#}\"),\n    Ok(()) => sink.new().await?,\n}","preventionTips":["Always call validate() before new() when constructing sinks programmatically","Log the resolved config keys at sink creation time","Cover both emulator and credential paths in integration tests"],"tags":["pubsub","gcp","config"],"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"}