github/copilot-sdk · error

URIConnection requires a non-empty URL

Error message

URIConnection requires a non-empty URL

What it means

NewClient panics when a URIConnection is supplied with an empty URL. A URIConnection means 'attach to an already-running external runtime at this address', so without a URL there is nothing to connect to and construction cannot proceed.

Solutions

  1. Provide a valid URL, e.g. URIConnection{URL: "http://127.0.0.1:4141"}.
  2. Check the config/env source for the URL and fail earlier with a clear message if empty.
  3. If no external server exists, use the default connection (StdioConnection) instead.

Example fix

// before
client := clientpkg.NewClient(&clientpkg.Options{
    Connection: clientpkg.URIConnection{URL: os.Getenv("COPILOT_CLI_URL")}, // empty
})
// after
u := os.Getenv("COPILOT_CLI_URL")
if u == "" {
    u = "http://127.0.0.1:4141"
}
client := clientpkg.NewClient(&clientpkg.Options{
    Connection: clientpkg.URIConnection{URL: u},
})
Defensive patterns

Strategy: validation

Validate before calling

if conn.URL == "" {
    return fmt.Errorf("URIConnection.URL is required")
}
if _, err := url.Parse(conn.URL); err != nil {
    return fmt.Errorf("invalid URIConnection.URL: %w", err)
}

Prevention

When it happens

Trigger: Calling NewClient with Connection: clientpkg.URIConnection{URL: ""} — typically because the URL comes from a config value or env var that is unset or empty. Panic at go/client.go:274.

Common situations: Environment variable like COPILOT_CLI_URL is not set and its value is passed straight into URIConnection; wiring an external-server option that was left blank in YAML/flags.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at go/client.go:274

	}
	switch conn := connection.(type) {
	case StdioConnection:
		client.useStdio = true
		client.cliPath = conn.Path
		if len(conn.Args) > 0 {
			client.cliArgs = append([]string{}, conn.Args...)
		}
	case TCPConnection:
		client.useStdio = false
		client.cliPath = conn.Path
		if len(conn.Args) > 0 {
			client.cliArgs = append([]string{}, conn.Args...)
		}
		client.port = conn.Port
		client.tcpConnectionToken = conn.ConnectionToken
	case URIConnection:
		if conn.URL == "" {
			panic("URIConnection requires a non-empty URL")
		}
		host, port := parseCLIURL(conn.URL)
		client.actualHost = host
		client.actualPort = port
		client.isExternalServer = true
		client.useStdio = false
		client.tcpConnectionToken = conn.ConnectionToken
	case InProcessConnection:
		client.useStdio = false
		client.useInProcess = true
	default:
		panic(fmt.Sprintf("unknown RuntimeConnection type: %T", connection))
	}

	// Validate transport-specific option constraints (fail loud). The in-process
	// transport loads the runtime into this process, whose single environment
	// block, process-global working directory, and shared telemetry state cannot
	// carry per-client values. Child-process transports may set env via either

View on GitHub (pinned to cd8cf15dc3)