github/copilot-sdk · error · IllegalArgumentException

TcpConnectionToken cannot be used with UseStdio = true

Error message

TcpConnectionToken cannot be used with UseStdio = true

What it means

A TCP connection token implies connecting over TCP, while UseStdio = true implies launching and talking to the CLI over stdio. The two transports are mutually exclusive, so the constructor rejects the combination with IllegalArgumentException.

Solutions

  1. Remove the tcpConnectionToken if you intend stdio transport (setUseStdio(true))
  2. Set UseStdio = false if you intend TCP transport and keep the token
  3. Split into two options objects, one per transport mode

Example fix

// before
options.setUseStdio(true);
options.setTcpConnectionToken(token);
// after
options.setUseStdio(false); // TCP mode
options.setTcpConnectionToken(token);
Defensive patterns

Strategy: validation

Validate before calling

if (options.getTcpConnectionToken() != null && options.isUseStdio()) throw new IllegalStateException("tcpConnectionToken and UseStdio are mutually exclusive");

Try / catch

try { new CopilotClient(options); } catch (IllegalArgumentException e) { if (e.getMessage().contains("cannot be used with UseStdio")) { options.setTcpConnectionToken(null); } else throw e; }

Prevention

When it happens

Trigger: new CopilotClient(...) with options where both setTcpConnectionToken(nonEmpty) and setUseStdio(true) are set.

Common situations: Copy-pasted options from a TCP-mode example onto a stdio-mode app; a shared options builder that always sets the token; enabling stdio debugging while keeping an old TCP token.

Related errors


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

Appendix: source

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

        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");
            }
        }

        // Compute effective connection token: use provided, or auto-generate for
        // SDK-spawned TCP mode, or null for stdio/external server
        boolean sdkSpawnsCli = !this.options.isUseStdio()
                && (this.options.getCliUrl() == null || this.options.getCliUrl().isEmpty());
        this.effectiveConnectionToken = this.options.getTcpConnectionToken() != null
                ? this.options.getTcpConnectionToken()
                : (sdkSpawnsCli ? java.util.UUID.randomUUID().toString() : null);

        // Empty mode: validate at construction time that the app supplied a
        // per-session persistence location.
        if (this.options.getMode() == CopilotClientMode.EMPTY) {
            boolean hasPersistence = (this.options.getCopilotHome() != null && !this.options.getCopilotHome().isEmpty())
                    || (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty());
            if (!hasPersistence) {
                throw new IllegalArgumentException(

View on GitHub (pinned to cd8cf15dc3)