{"record":{"id":"88260b4c0038467b","repo":"github/copilot-sdk","slug":"cliurl-is-mutually-exclusive-with-clipath","errorCode":null,"errorMessage":"CliUrl is mutually exclusive with CliPath","messagePattern":"CliUrl is mutually exclusive with CliPath","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"java/sdk/src/main/java/com/github/copilot/CopilotClient.java","lineNumber":181,"sourceCode":"            requestedConnection = resolveDefaultConnection(this.options);\n            validateEnvironmentOptions(this.options, requestedConnection);\n            // When the env var overrides inference (e.g. inprocess), validate that\n            // no legacy transport options conflict with the resolved connection.\n            if (requestedConnection != null) {\n                validateConnectionConflicts(this.options, requestedConnection);\n            }\n        }\n        this.runtimeConnection = requestedConnection;\n\n        // When cliUrl is set, auto-correct useStdio since we're connecting via TCP\n        if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()) {\n            this.options.setUseStdio(false);\n        }\n\n        // Validate mutually exclusive options: cliUrl and cliPath cannot both be set\n        if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()\n                && this.options.getCliPath() != null) {\n            throw new IllegalArgumentException(\"CliUrl is mutually exclusive with CliPath\");\n        }\n\n        // Validate auth options with external server\n        if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()\n                && (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser().isPresent())) {\n            throw new IllegalArgumentException(\n                    \"GitHubToken and UseLoggedInUser cannot be used with CliUrl (external server manages its own auth)\");\n        }\n\n        // Validate tcpConnectionToken\n        if (this.options.getTcpConnectionToken() != null) {\n            if (this.options.getTcpConnectionToken().isEmpty()) {\n                throw new IllegalArgumentException(\"TcpConnectionToken must be a non-empty string\");\n            }\n            if (this.options.isUseStdio()) {\n                throw new IllegalArgumentException(\"TcpConnectionToken cannot be used with UseStdio = true\");\n            }\n        }","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/CopilotClient.java#L163-L199","documentation":"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.","triggerScenarios":"Building CopilotOptions with both a non-empty cliUrl and a non-null cliPath, then constructing CopilotClient with those options.","commonSituations":"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.","solutions":["Remove cliPath from options when using cliUrl (external server mode).","Remove cliUrl when you want the client to spawn the CLI via cliPath.","Centralize options construction so only one transport field is set based on a mode flag."],"exampleFix":"// before\noptions.setCliUrl(\"http://localhost:8080\");\noptions.setCliPath(\"/usr/local/bin/copilot\"); // conflicts\n// after\noptions.setCliUrl(\"http://localhost:8080\");\noptions.setCliPath(null); // external-server mode only","handlingStrategy":"validation","validationCode":"if (options.getCliUrl() != null && !options.getCliUrl().isEmpty() && options.getCliPath() != null) {\n    throw new IllegalArgumentException(\"Set either cliUrl or cliPath, not both\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  client = new CopilotClient(options);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"mutually exclusive\")) {\n    options.setCliPath(null);\n    client = new CopilotClient(options);\n  } else throw e;\n}","preventionTips":["Build options from a single mode flag (external vs local) so only one transport is set.","Null out the other transport field when switching modes.","Validate options in your own builder before constructing CopilotClient."],"tags":["java","configuration","validation","client-construction"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}