github/copilot-sdk · error

Client is in Mode=ModeEmpty but the session config did not…

Error message

Client is in Mode=ModeEmpty but the session config did not specify AvailableTools. Empty mode requires every session to explicitly opt into the tools it wants — e.g. NewToolSet().AddBuiltIn(BuiltInToolsIsolated...).ToSlice()

What it means

When the Client is configured with Mode=ModeEmpty, resolveToolFilterOptions requires every session to explicitly declare its available tools. ModeEmpty gives no tools by default, so CreateSession/ResumeSessionWithOptions fail if availableTools is nil.

Solutions

  1. Build an explicit tool list, e.g. NewToolSet().AddBuiltIn(BuiltInToolsIsolated...).ToSlice(), and pass it as AvailableTools
  2. Change the client Mode if unrestricted tool resolution is intended
  3. Pass an empty (non-nil) tool slice if a tool-less session is deliberate, if supported

Example fix

// before
session, _, _, err := client.CreateSession(ctx, nil) // ModeEmpty, no tools
// after
tools := copilot.NewToolSet().AddBuiltIn(copilot.BuiltInToolsIsolated...).ToSlice()
session, _, _, err := client.CreateSession(ctx, copilot.SessionOptions{AvailableTools: tools})
Defensive patterns

Strategy: validation

Validate before calling

if client.Options().Mode == copilot.ModeEmpty && sessionOpts.AvailableTools == nil {
    return fmt.Errorf("ModeEmpty requires AvailableTools")
}

Prevention

When it happens

Trigger: Creating or resuming a session (CreateSession, ResumeSessionWithOptions) on a ModeEmpty client without passing AvailableTools in the session options.

Common situations: Switching a client to ModeEmpty for least-privilege isolation but reusing old session code that never set AvailableTools; passing only ExcludedTools and assuming defaults exist.

Understand the failure class

Background: "Must pass :limit option" / "Missing required option" — required option errors explained — 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/16370064acfdd4b4. Report an issue: GitHub.

Appendix: source

Thrown at go/mode_empty.go:68

				field, entry)
		}
	}
	return nil
}

// resolveToolFilterOptions validates the configured tool filters and applies
// empty-mode invariants. Returns the (possibly-mutated) request fields to set.
func (c *Client) resolveToolFilterOptions(availableTools, excludedTools []string) (
	[]string, []string, *rpc.OptionsUpdateToolFilterPrecedence, error,
) {
	if err := validateToolFilterList("availableTools", availableTools); err != nil {
		return nil, nil, nil, err
	}
	if err := validateToolFilterList("excludedTools", excludedTools); err != nil {
		return nil, nil, nil, err
	}
	if c.options.Mode == ModeEmpty && availableTools == nil {
		return nil, nil, nil, errors.New(
			"Client is in Mode=ModeEmpty but the session config did not specify AvailableTools. " +
				"Empty mode requires every session to explicitly opt into the tools it wants — " +
				"e.g. NewToolSet().AddBuiltIn(BuiltInToolsIsolated...).ToSlice()")
	}
	precedence := rpc.OptionsUpdateToolFilterPrecedenceExcluded
	return availableTools, excludedTools, &precedence, nil
}

// systemMessageForMode applies empty-mode environment_context stripping to
// the caller-supplied system message config. App values win (we only inject
// when the app hasn't already specified an environment_context override).
func (c *Client) systemMessageForMode(supplied *SystemMessageConfig) *SystemMessageConfig {
	if c.options.Mode != ModeEmpty {
		return supplied
	}
	removeAction := SectionOverride{Action: SectionActionRemove}
	if supplied == nil {
		return &SystemMessageConfig{

View on GitHub (pinned to cd8cf15dc3)