github/copilot-sdk · error · IllegalArgumentException

CliUrl is mutually exclusive with CliPath

Error message

CliUrl is mutually exclusive with CliPath

What it means

CopilotClient's constructor validates that its options are coherent. CliUrl (connect to an external already-running server) and CliPath (spawn a local CLI process) are mutually exclusive transports, so setting both is an IllegalArgumentException at construction time. This fails fast instead of producing ambiguous connection behavior.

Solutions

  1. Remove cliPath from options when using cliUrl (external server mode).
  2. Remove cliUrl when you want the client to spawn the CLI via cliPath.
  3. Centralize options construction so only one transport field is set based on a mode flag.

Example fix

// before
options.setCliUrl("http://localhost:8080");
options.setCliPath("/usr/local/bin/copilot"); // conflicts
// after
options.setCliUrl("http://localhost:8080");
options.setCliPath(null); // external-server mode only
Defensive patterns

Strategy: validation

Validate before calling

if (options.getCliUrl() != null && !options.getCliUrl().isEmpty() && options.getCliPath() != null) {
    throw new IllegalArgumentException("Set either cliUrl or cliPath, not both");
}

Try / catch

try {
  client = new CopilotClient(options);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("mutually exclusive")) {
    options.setCliPath(null);
    client = new CopilotClient(options);
  } else throw e;
}

Prevention

When it happens

Trigger: Building CopilotOptions with both a non-empty cliUrl and a non-null cliPath, then constructing CopilotClient with those options.

Common situations: Copy-pasting option setups from a stdio example and a TCP example; merging config sources where one sets cliPath from a default and another sets cliUrl; toggling between modes without clearing the other field.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/88260b4c0038467b. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/CopilotClient.java:181

            requestedConnection = resolveDefaultConnection(this.options);
            validateEnvironmentOptions(this.options, requestedConnection);
            // When the env var overrides inference (e.g. inprocess), validate that
            // no legacy transport options conflict with the resolved connection.
            if (requestedConnection != null) {
                validateConnectionConflicts(this.options, requestedConnection);
            }
        }
        this.runtimeConnection = requestedConnection;

        // When cliUrl is set, auto-correct useStdio since we're connecting via TCP
        if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()) {
            this.options.setUseStdio(false);
        }

        // Validate mutually exclusive options: cliUrl and cliPath cannot both be set
        if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()
                && this.options.getCliPath() != null) {
            throw new IllegalArgumentException("CliUrl is mutually exclusive with CliPath");
        }

        // Validate auth options with external server
        if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()
                && (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser().isPresent())) {
            throw new IllegalArgumentException(
                    "GitHubToken and UseLoggedInUser cannot be used with CliUrl (external server manages its own auth)");
        }

        // Validate tcpConnectionToken
        if (this.options.getTcpConnectionToken() != null) {
            if (this.options.getTcpConnectionToken().isEmpty()) {
                throw new IllegalArgumentException("TcpConnectionToken must be a non-empty string");
            }
            if (this.options.isUseStdio()) {
                throw new IllegalArgumentException("TcpConnectionToken cannot be used with UseStdio = true");
            }
        }

View on GitHub (pinned to cd8cf15dc3)