{"record":{"id":"4d2d08d5489fda58","repo":"github/copilot-sdk","slug":"client-is-in-mode-modeempty-but-neither-basedirect","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/mode_empty.go","lineNumber":35,"sourceCode":"func validateNewClientForMode(opts *ClientOptions) {\n\tif opts == nil || opts.Mode != ModeEmpty {\n\t\treturn\n\t}\n\t// Empty mode requires durable, app-owned storage. Either:\n\t//   - the app supplied a BaseDirectory the runtime can write to,\n\t//   - the app supplied a SessionFS implementation,\n\t//   - or the app is connecting to an externally-managed runtime via\n\t//     URIConnection (in which case the host owns storage).\n\tif opts.BaseDirectory != \"\" {\n\t\treturn\n\t}\n\tif opts.SessionFS != nil {\n\t\treturn\n\t}\n\tif _, ok := opts.Connection.(URIConnection); ok {\n\t\treturn\n\t}\n\tpanic(\"Client is in Mode=ModeEmpty but neither BaseDirectory, SessionFS, nor a URIConnection was supplied. \" +\n\t\t\"Empty mode requires explicit, per-tenant storage; set ClientOptions.BaseDirectory or .SessionFS, \" +\n\t\t\"or connect to an externally-managed runtime via URIConnection.\")\n}\n\n// validateToolFilterList rejects bare \"*\" entries with an actionable error\n// pointing at the [ToolSet] builder. Called for both availableTools and\n// excludedTools.\nfunc validateToolFilterList(field string, list []string) error {\n\tfor _, entry := range list {\n\t\tif entry == \"*\" {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"invalid %s entry %q: there is no bare wildcard. \"+\n\t\t\t\t\t\"Use one or more of NewToolSet().AddBuiltIn(\\\"*\\\"), .AddMCP(\\\"*\\\"), or .AddCustom(\\\"*\\\") \"+\n\t\t\t\t\t\"to target a specific source\",\n\t\t\t\tfield, entry)\n\t\t}\n\t}\n\treturn nil","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/go/mode_empty.go#L17-L53","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nclient := NewClient(ClientOptions{Mode: ModeEmpty})\n\n// after\nclient := NewClient(ClientOptions{\n    Mode:          ModeEmpty,\n    BaseDirectory: \"/var/lib/myapp/tenants/\" + tenantID,\n})","handlingStrategy":"validation","validationCode":"func optsValidForEmptyMode(opts ClientOptions) bool {\n    _, isURI := opts.Connection.(URIConnection)\n    return opts.Mode != ModeEmpty || opts.BaseDirectory != \"\" || opts.SessionFS != nil || isURI\n}","typeGuard":"func isURIConnection(c Connection) bool {\n    _, ok := c.(URIConnection)\n    return ok\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        if s, ok := r.(string); ok && strings.Contains(s, \"ModeEmpty\") {\n            log.Fatalf(\"empty-mode client needs storage: %s\", s)\n        }\n        panic(r)\n    }\n}()","preventionTips":["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."],"tags":["go","panic","configuration","storage","client-construction"],"backgroundTag":"missing-required-config","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"}