{"record":{"id":"fcec5a10bd64dbe7","repo":"apache/seatunnel","slug":"options-credentials-path-and-emulator-host-can","errorCode":null,"errorMessage":"Options 'credentials_path' and 'emulator_host' cannot be configured together","messagePattern":"Options 'credentials_path' and 'emulator_host' cannot be configured together","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-google-pubsub/src/main/java/org/apache/seatunnel/connectors/seatunnel/google/pubsub/config/GooglePubSubSinkConfig.java","lineNumber":51,"sourceCode":"    private final String credentialsPath;\n    private final String emulatorHost;\n    private final MessageFormat format;\n    private final String fieldDelimiter;\n\n    public static GooglePubSubSinkConfig from(ReadonlyConfig config) {\n        String projectId = config.get(GooglePubSubSinkOptions.PROJECT_ID);\n        String topic = config.get(GooglePubSubSinkOptions.TOPIC);\n        String credentialsPath = config.get(GooglePubSubSinkOptions.CREDENTIALS_PATH);\n        String emulatorHost = config.get(GooglePubSubSinkOptions.EMULATOR_HOST);\n        MessageFormat format = config.get(GooglePubSubSinkOptions.FORMAT);\n        String fieldDelimiter = config.get(GooglePubSubSinkOptions.FIELD_DELIMITER);\n\n        requireNonBlank(projectId, GooglePubSubSinkOptions.PROJECT_ID.key());\n        requireNonBlank(topic, GooglePubSubSinkOptions.TOPIC.key());\n        requireNonBlankIfPresent(credentialsPath, GooglePubSubSinkOptions.CREDENTIALS_PATH.key());\n        requireNonBlankIfPresent(emulatorHost, GooglePubSubSinkOptions.EMULATOR_HOST.key());\n        if (credentialsPath != null && emulatorHost != null) {\n            throw new IllegalArgumentException(\n                    \"Options 'credentials_path' and 'emulator_host' cannot be configured together\");\n        }\n        if (format == MessageFormat.TEXT && fieldDelimiter.isEmpty()) {\n            throw new IllegalArgumentException(\"Option 'field_delimiter' cannot be empty\");\n        }\n\n        return GooglePubSubSinkConfig.builder()\n                .projectId(projectId)\n                .topic(topic)\n                .credentialsPath(credentialsPath)\n                .emulatorHost(emulatorHost)\n                .format(format)\n                .fieldDelimiter(fieldDelimiter)\n                .build();\n    }\n\n    private static void requireNonBlank(String value, String option) {\n        if (value == null || value.trim().isEmpty()) {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-google-pubsub/src/main/java/org/apache/seatunnel/connectors/seatunnel/google/pubsub/config/GooglePubSubSinkConfig.java#L33-L69","documentation":"GooglePubSubSinkConfig.from() validates that credentials_path and emulator_host are mutually exclusive. credentials_path selects real GCP service-account authentication while emulator_host targets a local Pub/Sub emulator; specifying both is ambiguous and rejected with IllegalArgumentException during config parsing.","triggerScenarios":"A PubSub sink config contains non-null values for both credentials_path and emulator_host; from() is called when the sink is created, before any connection is made.","commonSituations":"Merging a production config (credentials_path) with a local-dev override (emulator_host) via environment variables or templating; leaving emulator_host set from a local test when deploying with real credentials.","solutions":["Remove emulator_host when running against real GCP, keeping only credentials_path.","Remove credentials_path when targeting the local emulator.","If both come from variables, make them conditionally exclusive in your config template (e.g. only emit emulator_host when EMULATOR is set)."],"exampleFix":"// before\ncredentials_path = \"/path/sa.json\"\nemulator_host = \"localhost:8085\"\n// after (real GCP)\ncredentials_path = \"/path/sa.json\"\n// or (emulator)\nemulator_host = \"localhost:8085\"","handlingStrategy":"validation","validationCode":"// validate before building the sink config\nboolean hasCreds = config.getString(\"credentials_path\") != null;\nboolean hasEmu = config.getString(\"emulator_host\") != null;\nif (hasCreds && hasEmu) throw new IllegalArgumentException(\"Set only one of credentials_path / emulator_host\");","typeGuard":null,"tryCatchPattern":"try {\n  GooglePubSubSinkConfig.from(config);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"cannot be configured together\")) {\n    log.error(\"Remove either credentials_path or emulator_host from the sink config\");\n  } else throw e;\n}","preventionTips":["Keep emulator_host only in local-dev profiles/overrides.","Use environment-scoped config templates that emit one auth mode.","Audit merged/layered configs for accidental option combinations."],"tags":["pubsub","sink","config","validation"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}