github/copilot-sdk · error

failed to unmarshal models response

Error message

failed to unmarshal models response: %w

What it means

ListModels wraps a json.Unmarshal failure of the raw models.list RPC result with this error. The backend returned JSON that does not fit the expected listModelsResponse shape. It almost always indicates a server/SDK protocol or payload mismatch rather than a caller mistake.

Solutions

  1. Verify the CLI backend and SDK versions match; upgrade the SDK or server so the models response schema aligns.
  2. Log the raw result payload (before unmarshal) to see the actual JSON shape returned.
  3. Check for proxies/wrappers altering the response body between server and SDK.
  4. Update server to a version whose models.list response includes the expected fields (e.g. models array of ModelInfo).

Example fix

// before
var response listModelsResponse
if err := json.Unmarshal(result, &response); err != nil {
    return nil, fmt.Errorf("failed to unmarshal models response: %w", err)
}

// after (debug the raw payload)
if err := json.Unmarshal(result, &response); err != nil {
    return nil, fmt.Errorf("failed to unmarshal models response: %w (payload: %s)", err, truncate(result, 512))
}
Defensive patterns

Strategy: validation

Validate before calling

// verify version compatibility before RPC
if client.GetNegotiatedProtocolVersion() == 0 {
    return errors.New("handshake incomplete; payload schema may differ")
}

Try / catch

models, err := client.ListModels(ctx)
var umErr *json.UnmarshalTypeError
if errors.As(err, &umErr) || strings.Contains(err.Error(), "failed to unmarshal models response") {
    // log payload + versions, surface as protocol mismatch
    return fmt.Errorf("protocol/schema mismatch: %w", err)
}

Prevention

When it happens

Trigger: The models.list RPC succeeds at the transport level but its result bytes cannot be decoded into listModelsResponse — e.g. server returns an object with different field names/types, a bare array, or truncated/non-JSON output.

Common situations: Mixing an older CLI binary with a newer SDK (or vice versa), a proxy injecting HTML error pages, or a server bug changing the models payload schema.

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/310d13d6ceaf1fa0. Report an issue: GitHub.

Appendix: source

Thrown at go/client.go:1946

		// Use custom handler instead of CLI RPC
		var err error
		models, err = c.onListModels(ctx)
		if err != nil {
			return nil, err
		}
	} else {
		if c.client == nil {
			return nil, fmt.Errorf("client not connected")
		}
		// Cache miss - fetch from backend while holding lock
		result, err := c.client.Request(ctx, "models.list", listModelsRequest{})
		if err != nil {
			return nil, err
		}

		var response listModelsResponse
		if err := json.Unmarshal(result, &response); err != nil {
			return nil, fmt.Errorf("failed to unmarshal models response: %w", err)
		}
		models = response.Models
	}

	// Update cache before releasing lock (copy to prevent external mutation)
	cache := make([]ModelInfo, len(models))
	copy(cache, models)
	c.modelsCache = cache

	// Return a copy to prevent cache mutation
	result := make([]ModelInfo, len(models))
	copy(result, models)
	return result, nil
}

// minProtocolVersion is the minimum protocol version this SDK can communicate with.
const minProtocolVersion = 3
const runtimeShutdownTimeout = 10 * time.Second

View on GitHub (pinned to cd8cf15dc3)