github/copilot-sdk · error

failed to decode session detach response

Error message

failed to decode session detach response: %w

What it means

Session.Disconnect sends the `session.detach` RPC; on success it unmarshals the result into sessionDetachResponse. This error means the detach response JSON could not be decoded — the runtime's reply does not match the SDK's expected `{success, error}` shape.

Solutions

  1. Align SDK and runtime versions (upgrade both)
  2. Capture the raw detach response to compare against the expected shape
  3. Retry Disconnect once; if it persists, close the underlying client connection instead
  4. File an issue with the raw payload if versions already match

Example fix

// before
if err := session.Disconnect(); err != nil { log.Fatal(err) }
// after
if err := session.Disconnect(); err != nil {
    log.Printf("detach decode failed, forcing client close: %v", err)
    _ = client.Close()
}
Defensive patterns

Strategy: fallback

Try / catch

if err := session.Disconnect(); err != nil {
    if strings.Contains(err.Error(), "decode session detach") {
        _ = client.Close() // fall back to local teardown
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: Calling Session.Disconnect() when `session.detach` returns a payload that fails json.Unmarshal into sessionDetachResponse (wrong field types or unexpected schema).

Common situations: Version skew between SDK and runtime changing the detach response; a non-standard runtime build returning a different detach payload; truncated/corrupt response.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at go/session.go:1841

// session data including files on disk, use [Client.DeleteSession] instead.
//
// After calling this method, the session object can no longer be used.
//
// Returns an error if the connection fails.
//
// Example:
//
//	// Clean up when done — session can still be resumed later
//	if err := session.Disconnect(); err != nil {
//	    log.Printf("Failed to disconnect session: %v", err)
//	}
func (s *Session) Disconnect() error {
	s.cancelPendingExternalTools()
	result, err := s.client.Request(context.Background(), "session.detach", sessionDetachRequest{SessionID: s.SessionID})
	if err == nil {
		var response sessionDetachResponse
		if decodeErr := json.Unmarshal(result, &response); decodeErr != nil {
			err = fmt.Errorf("failed to decode session detach response: %w", decodeErr)
		} else if !response.Success {
			if response.Error == "" {
				response.Error = "unknown error"
			}
			err = errors.New(response.Error)
		}
	}

	// Local cleanup always runs, even if the detach RPC failed, so callers
	// don't leak in-memory resources (event goroutines, registered
	// providers/handlers) just because the runtime couldn't be reached.
	s.stopEventProcessing()
	s.releaseGitHubTokenProviderRegistration()

	// Clear handlers
	s.handlerMutex.Lock()
	s.handlers = nil
	s.handlerMutex.Unlock()

View on GitHub (pinned to cd8cf15dc3)