github/copilot-sdk · error
Client is in Mode=ModeEmpty but neither BaseDirectory…
Error message
Client is in Mode=ModeEmpty but neither BaseDirectory, SessionFS, nor a URIConnection was supplied. Empty mode requires explicit, per-tenant storage; set ClientOptions.BaseDirectory or .SessionFS, or connect to an externally-managed runtime via URIConnection.
What it means
NewClient validates the requested mode before constructing the client. In Mode=ModeEmpty the library refuses to guess storage: empty mode demands explicit per-tenant storage via ClientOptions.BaseDirectory or ClientOptions.SessionFS, or an externally-managed runtime via a URIConnection. If none of the three is supplied the constructor panics with an actionable message instead of silently creating a client with unusable storage.
Solutions
- Set ClientOptions.BaseDirectory to a writable per-tenant directory path.
- Or supply a ClientOptions.SessionFS implementation for explicit session storage.
- Or pass a URIConnection in ClientOptions.Connection to attach to an externally-managed runtime.
- Or drop Mode: ModeEmpty and use the library's default mode if per-tenant storage is not actually required.
Example fix
// before
client := NewClient(ClientOptions{Mode: ModeEmpty})
// after
client := NewClient(ClientOptions{
Mode: ModeEmpty,
BaseDirectory: "/var/lib/myapp/tenants/" + tenantID,
}) Defensive patterns
Strategy: validation
Validate before calling
func optsValidForEmptyMode(opts ClientOptions) bool {
_, isURI := opts.Connection.(URIConnection)
return opts.Mode != ModeEmpty || opts.BaseDirectory != "" || opts.SessionFS != nil || isURI
} Type guard
func isURIConnection(c Connection) bool {
_, ok := c.(URIConnection)
return ok
} Try / catch
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "ModeEmpty") {
log.Fatalf("empty-mode client needs storage: %s", s)
}
panic(r)
}
}() Prevention
- Whenever you set Mode: ModeEmpty, immediately add BaseDirectory or SessionFS in the same options literal.
- Use a builder function that takes tenantID and always fills BaseDirectory.
- Cover client construction in a unit test that runs with production options.
When it happens
Trigger: Calling NewClient(ClientOptions{Mode: ModeEmpty}) with Connection not a URIConnection, SessionFS nil, and BaseDirectory empty — e.g. switching from default mode to ModeEmpty without adding storage options, or constructing a client with only credentials/tool options.
Common situations: Migrating existing code to multi-tenant ModeEmpty and forgetting BaseDirectory; copy-pasted options that dropped the SessionFS field; passing a plain (non-URI) Connection while intending an external runtime hookup.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- Env is not supported with InProcessConnection: the…
- WorkingDirectory is not supported with InProcessConnection…
- Telemetry is not supported with InProcessConnection…
- Set environment variables via either the client-level Env…
- GitHubToken and UseLoggedInUser cannot be used with…
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/4d2d08d5489fda58.
Report an issue: GitHub.
Appendix: source
Thrown at go/mode_empty.go:35
func validateNewClientForMode(opts *ClientOptions) {
if opts == nil || opts.Mode != ModeEmpty {
return
}
// Empty mode requires durable, app-owned storage. Either:
// - the app supplied a BaseDirectory the runtime can write to,
// - the app supplied a SessionFS implementation,
// - or the app is connecting to an externally-managed runtime via
// URIConnection (in which case the host owns storage).
if opts.BaseDirectory != "" {
return
}
if opts.SessionFS != nil {
return
}
if _, ok := opts.Connection.(URIConnection); ok {
return
}
panic("Client is in Mode=ModeEmpty but neither BaseDirectory, SessionFS, nor a URIConnection was supplied. " +
"Empty mode requires explicit, per-tenant storage; set ClientOptions.BaseDirectory or .SessionFS, " +
"or connect to an externally-managed runtime via URIConnection.")
}
// validateToolFilterList rejects bare "*" entries with an actionable error
// pointing at the [ToolSet] builder. Called for both availableTools and
// excludedTools.
func validateToolFilterList(field string, list []string) error {
for _, entry := range list {
if entry == "*" {
return fmt.Errorf(
"invalid %s entry %q: there is no bare wildcard. "+
"Use one or more of NewToolSet().AddBuiltIn(\"*\"), .AddMCP(\"*\"), or .AddCustom(\"*\") "+
"to target a specific source",
field, entry)
}
}
return nilView on GitHub (pinned to cd8cf15dc3)