Repository: slack-go/slack (2025-08-30T15:03:23Z)

Issue:
Unmarshal error received when removing a user from a conversation
### What happened
When using the `KickUserFromConversation` method, I get an error like this:

`err: json: cannot unmarshal object into Go struct field SlackResponse.errors of type []slack.SlackResponseErrors`

I'm guessing that this is due to the response being this:

`{"ok":true,"errors":{}}`

and the `errors` field cannot be deserialized to array

The doc for https://api.slack.com/methods/conversations.kick#examples also gives an example success response of 
```
{
    "ok": true,
    "errors": {}
}
```


### Expected behavior
I'd expect to get no error, since the operation was successful

### Steps to reproduce
For me it was just a call to `KickUserFromConversation` with a valid channel ID and user ID (i.e. user is in the channel)

#### reproducible code

#### manifest.yaml

### Versions
- Go: 1.23.9
- slack-go/slack: 0.17.3



Discussion:
Oof this is really annoying. I'll put a PR for this and would love if you could give it a try before I merge.
Sorry, perhaps a bit late but - tried it and I think it looks good! (am no longer getting an error)



Patch:
diff --git a/conversation.go b/conversation.go
index ae53f076..d16b88d8 100644
--- a/conversation.go
+++ b/conversation.go
@@ -479,7 +479,7 @@ func (api *Client) KickUserFromConversationContext(ctx context.Context, channelI
 		"user":    {user},
 	}
 
-	response := SlackResponse{}
+	response := KickUserFromConversationSlackResponse{}
 	err := api.postMethod(ctx, "conversations.kick", values, &response)
 	if err != nil {
 		return err
diff --git a/misc.go b/misc.go
index 48411ee1..586551cf 100644
--- a/misc.go
+++ b/misc.go
@@ -118,6 +118,68 @@ type SlackResponse struct {
 	ResponseMetadata ResponseMetadata      `json:"response_metadata"`
 }
 
+// KickUserFromConversationSlackResponse is a variant of SlackResponse that can handle the case where
+// "errors" can be either an empty object {} or an array of errors.
+// This addresses issue #1446 where conversations.kick endpoint returns {"ok":true,"errors":{}}
+type KickUserFromConversationSlackResponse struct {
+	Ok               bool                  `json:"ok"`
+	Error            string                `json:"error"`
+	Errors           []SlackResponseErrors `json:"-"`
+	ResponseMetadata ResponseMetadata      `json:"response_metadata"`
+}
+
+// UnmarshalJSON implements custom unmarshaling for KickUserFromConversationSlackResponse to handle
+// the case where "errors" can be either an empty object {} or an array of errors
+func (s *KickUserFromConversationSlackResponse) UnmarshalJSON(data []byte) error {
+	// First, unmarshal everything except errors
+	type Alias KickUserFromConversationSlackResponse
+	aux := &struct {
+		*Alias
+		ErrorsRaw json.RawMessage `json:"errors,omitempty"`
+	}{
+		Alias: (*Alias)(s),
+	}
+
+	if err := json.Unmarshal(data, &aux); err != nil {
+		return err
+	}
+
+	// Handle the errors field
+	if len(aux.ErrorsRaw) > 0 {
+		// Check if it's an empty object by looking for just "{}"
+		trimmed := bytes.TrimSpace(aux.ErrorsRaw)
+		if bytes.Equal(trimmed, []byte("{}")) {
+			// Empty object, leave errors as nil/empty slice
+			s.Errors = nil
+		} else {
+			// Try to unmarshal as array of errors
+			var errors []SlackResponseErrors
+			if err := json.Unmarshal(aux.ErrorsRaw, &errors); err != nil {
+				return err
+			}
+			s.Errors = errors
+		}
+	}
+
+	return nil
+}
+
+// Err returns any API error present in the response.
+func (s KickUserFromConversationSlackResponse) Err() error {
+	if s.Ok {
+		return nil
+	}
+
+	// handle pure text based responses like chat.post
+	// which while they have a slack response in their data structure
+	// it doesn't actually get set during parsing.
+	if strings.TrimSpace(s.Error) == "" {
+		return nil
+	}
+
+	return SlackErrorResponse{Err: s.Error, Errors: s.Errors, ResponseMetadata: s.ResponseMetadata}
+}
+
 func (t SlackResponse) Err() error {
 	if t.Ok {
 		return nil


Test patch:
diff --git a/misc_test.go b/misc_test.go
index d872199a..631115d9 100644
--- a/misc_test.go
+++ b/misc_test.go
@@ -336,3 +336,67 @@ func TestSlackResponseWithErrors(t *testing.T) {
 		})
 	}
 }
+
+func TestKickUserFromConversationSlackResponseWithErrors(t *testing.T) {
+	tests := []struct {
+		name     string
+		input    string
+		expected KickUserFromConversationSlackResponse
+	}{
+		{
+			name:  "ResponseWithEmptyErrorsObject",
+			input: `{"ok":true,"errors":{}}`,
+			expected: KickUserFromConversationSlackResponse{
+				Ok:     true,
+				Errors: nil,
+			},
+		},
+		{
+			name:  "ResponseWithErrorsArray",
+			input: `{"ok":false,"error":"some_error","errors":[]}`,
+			expected: KickUserFromConversationSlackResponse{
+				Ok:     false,
+				Error:  "some_error",
+				Errors: []SlackResponseErrors{},
+			},
+		},
+		{
+			name:  "ResponseWithoutErrors",
+			input: `{"ok":true}`,
+			expected: KickUserFromConversationSlackResponse{
+				Ok: true,
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var response KickUserFromConversationSlackResponse
+			err := json.Unmarshal([]byte(tt.input), &response)
+			if err != nil {
+				t.Fatalf("Unmarshal failed: %v", err)
+			}
+
+			if response.Ok != tt.expected.Ok {
+				t.Errorf("got Ok=%v; want Ok=%v", response.Ok, tt.expected.Ok)
+			}
+			if response.Error != tt.expected.Error {
+				t.Errorf("got Error=%q; want Error=%q", response.Error, tt.expected.Error)
+			}
+
+			if tt.expected.Errors == nil {
+				if response.Errors != nil {
+					t.Error("expected nil Errors, got non-nil")
+				}
+			} else {
+				if response.Errors == nil {
+					t.Error("expected non-nil Errors, got nil")
+					return
+				}
+				if len(tt.expected.Errors) != len(response.Errors) {
+					t.Errorf("got Errors length=%d; want length=%d", len(response.Errors), len(tt.expected.Errors))
+				}
+			}
+		})
+	}
+}