apache/seatunnel · error · IllegalArgumentException
Options 'credentials_path' and 'emulator_host' cannot be con
Error message
Options 'credentials_path' and 'emulator_host' cannot be configured together
What it means
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.
Source
Thrown at seatunnel-connectors-v2/connector-google-pubsub/src/main/java/org/apache/seatunnel/connectors/seatunnel/google/pubsub/config/GooglePubSubSinkConfig.java:51
private final String credentialsPath;
private final String emulatorHost;
private final MessageFormat format;
private final String fieldDelimiter;
public static GooglePubSubSinkConfig from(ReadonlyConfig config) {
String projectId = config.get(GooglePubSubSinkOptions.PROJECT_ID);
String topic = config.get(GooglePubSubSinkOptions.TOPIC);
String credentialsPath = config.get(GooglePubSubSinkOptions.CREDENTIALS_PATH);
String emulatorHost = config.get(GooglePubSubSinkOptions.EMULATOR_HOST);
MessageFormat format = config.get(GooglePubSubSinkOptions.FORMAT);
String fieldDelimiter = config.get(GooglePubSubSinkOptions.FIELD_DELIMITER);
requireNonBlank(projectId, GooglePubSubSinkOptions.PROJECT_ID.key());
requireNonBlank(topic, GooglePubSubSinkOptions.TOPIC.key());
requireNonBlankIfPresent(credentialsPath, GooglePubSubSinkOptions.CREDENTIALS_PATH.key());
requireNonBlankIfPresent(emulatorHost, GooglePubSubSinkOptions.EMULATOR_HOST.key());
if (credentialsPath != null && emulatorHost != null) {
throw new IllegalArgumentException(
"Options 'credentials_path' and 'emulator_host' cannot be configured together");
}
if (format == MessageFormat.TEXT && fieldDelimiter.isEmpty()) {
throw new IllegalArgumentException("Option 'field_delimiter' cannot be empty");
}
return GooglePubSubSinkConfig.builder()
.projectId(projectId)
.topic(topic)
.credentialsPath(credentialsPath)
.emulatorHost(emulatorHost)
.format(format)
.fieldDelimiter(fieldDelimiter)
.build();
}
private static void requireNonBlank(String value, String option) {
if (value == null || value.trim().isEmpty()) {View on GitHub (pinned to cf67b549a7)
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).
Example fix
// before credentials_path = "/path/sa.json" emulator_host = "localhost:8085" // after (real GCP) credentials_path = "/path/sa.json" // or (emulator) emulator_host = "localhost:8085"
Defensive patterns
Strategy: validation
Validate before calling
// validate before building the sink config
boolean hasCreds = config.getString("credentials_path") != null;
boolean hasEmu = config.getString("emulator_host") != null;
if (hasCreds && hasEmu) throw new IllegalArgumentException("Set only one of credentials_path / emulator_host"); Try / catch
try {
GooglePubSubSinkConfig.from(config);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("cannot be configured together")) {
log.error("Remove either credentials_path or emulator_host from the sink config");
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Option 'field_delimiter' cannot be empty
- Option 'field_delimiter' cannot be empty
- Option 'max_in_flight' must be greater than zero
- Option 'operation_timeout_ms' must be greater than zero
- Option '${option}' cannot be blank
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/fcec5a10bd64dbe7.
Report an issue: GitHub.