Repository: slack-go/slack (go)

Issue:
SlackResponse errors struct does not handle all errors
### What happened
When posting a message using an invalid set of blocks, we get this go error:
`json: cannot unmarshal string into Go struct field chatResponseFull.SlackResponse.errors of type map[string]interface {}`

### Expected behavior
We'd get a go error describing the issue with post message

Code that caused this to happen was calling post message with this block:
```
block := []slack.Block{
    &slack.SectionBlock{
        Type: slack.MBTSection,
        Text: &slack.TextBlockObject{
            Type: slack.MarkdownType,
            Text: ":shrug: neutral response" ,
            Emoji:    &[]bool{false}[0],
            Verbatim: false,
       },
    },
}
```

The emoji field should not be there, we had it in our previous code for some reason and when converting to the new version of the library, the `Emoji bool` became `Emoji *bool` so we adjusted it.   (removing it causes the PostMessage API not fail, but this specific issue is about improper error parsing, adding it here as it's an easy way to trigger the issue)


## Cause
The error we are receiving in the slack API (socket mode) is:
`{"ok":false,"error":"invalid_blocks","errors":["failed to match all allowed schemas [json-pointer:\/blocks\/3\/text]","invalid additional property: emoji [json-pointer:\/blocks\/3\/text]"],"response_metadata":{"messages":["[ERROR] failed to match all allowed schemas [json-pointer:\/blocks\/3\/text]","[ERROR] invalid additional property: emoji [json-pointer:\/blocks\/3\/text]"]}}`


So, we're getting an array of strings back, but a change made recently assumes that we can parse the response as a map[string]interface{}
https://github.com/slack-go/slack/blob/master/misc.go#L68-L71
made in https://github.com/slack-go/slack/commit/da9768248627cbeedabb83781f7f92579be1af0d 

Will work on code to reproduce / a fix, just wanted to surface it now as it took a fair amount of digging to figure this out, and I wanted to save others some time if they run in to it as I suspect it's an easy to hit issue.

Fix for this, I assume, will be to do a fallback []string parse(or are they always []string?).

### Versions
- Go: 1.24.4
- slack-go/slack: 0.17.1


Pull request:
fix: parse simple string based errors as part of the response
A response can return a payload of the form:

```
{
  "ok": false,
  "error": "invalid_blocks",
  "errors": [
    "failed to match all allowed schemas [json-pointer:/blocks/3/text]",
    "invalid additional property: emoji [json-pointer:/blocks/3/text]"
  ],
  "response_metadata": {
    "messages": [
      "[ERROR] failed to match all allowed schemas [json-pointer:/blocks/3/text]",
      "[ERROR] invalid additional property: emoji [json-pointer:/blocks/3/text]"
    ]
  }
}
```

This means that we need to fallback to parsing a simple string if we fail to parse the content of "errors" into a `map[string]interface{}`.

Closes #1446.

Patch:
diff --git a/misc.go b/misc.go
index 7a0ab35..48411ee 100644
--- a/misc.go
+++ b/misc.go
@@ -45,6 +45,7 @@ func (t ConversationsInviteResponseError) Err() error {
 type SlackResponseErrors struct {
 	AppsManifestCreateResponseError  *AppsManifestCreateResponseError  `json:"-"`
 	ConversationsInviteResponseError *ConversationsInviteResponseError `json:"-"`
+	Message                          *string                           `json:"-"`
 }
 
 // MarshalJSON implements custom marshaling for SlackResponseErrors
@@ -55,6 +56,9 @@ func (e SlackResponseErrors) MarshalJSON() ([]byte, error) {
 	if e.ConversationsInviteResponseError != nil {
 		return json.Marshal(e.ConversationsInviteResponseError)
 	}
+	if e.Message != nil {
+		return json.Marshal(*e.Message)
+	}
 	return json.Marshal(nil)
 }
 
@@ -67,6 +71,15 @@ func (e *SlackResponseErrors) UnmarshalJSON(data []byte) error {
 	// Try to determine the error type by checking for unique fields
 	var raw map[string]interface{}
 	if err := json.Unmarshal(data, &raw); err != nil {
+		// If we can't unmarshal as object, try as string (fallback case)
+		//
+		// For more details on this specific problem look up issue
+		// https://github.com/slack-go/slack/issues/1446.
+		var stringError string
+		if stringErr := json.Unmarshal(data, &stringError); stringErr == nil {
+			e.Message = &stringError
+			return nil
+		}
 		return err
 	}
 


Test patch:
diff --git a/misc_test.go b/misc_test.go
index 06fab1e..d872199 100644
--- a/misc_test.go
+++ b/misc_test.go
@@ -133,6 +133,13 @@ func TestSlackResponseErrorsMarshaling(t *testing.T) {
 			},
 			expected: `{"error":"invalid_user","ok":false,"user":"U12345678"}`,
 		},
+		{
+			name: "StringError",
+			errors: SlackResponseErrors{
+				Message: func() *string { s := "failed to match all allowed schemas"; return &s }(),
+			},
+			expected: `"failed to match all allowed schemas"`,
+		},
 		{
 			name:     "EmptyErrors",
 			errors:   SlackResponseErrors{},
@@ -185,6 +192,16 @@ func TestSlackResponseErrorsUnmarshaling(t *testing.T) {
 			input:    `null`,
 			expected: SlackResponseErrors{},
 		},
+		{
+			name:  "StringError",
+			input: `"failed to match all allowed schemas [json-pointer:\\/blocks\\/3\\/text]"`,
+			expected: SlackResponseErrors{
+				Message: func() *string {
+					s := "failed to match all allowed schemas [json-pointer:\\/blocks\\/3\\/text]"
+					return &s
+				}(),
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -210,6 +227,14 @@ func TestSlackResponseErrorsUnmarshaling(t *testing.T) {
 					t.Errorf("got %+v; want %+v", *errors.ConversationsInviteResponseError, *tt.expected.ConversationsInviteResponseError)
 				}
 			}
+
+			if tt.expected.Message != nil {
+				if errors.Message == nil {
+					t.Error("expected Message, got nil")
+				} else if *errors.Message != *tt.expected.Message {
+					t.Errorf("got %+v; want %+v", *errors.Message, *tt.expected.Message)
+				}
+			}
 		})
 	}
 }
@@ -256,6 +281,28 @@ func TestSlackResponseWithErrors(t *testing.T) {
 				Ok: true,
 			},
 		},
+		{
+			name:  "ResponseWithStringErrors",
+			input: `{"ok":false,"error":"invalid_blocks","errors":["failed to match all allowed schemas [json-pointer:\\/blocks\\/3\\/text]","invalid additional property: emoji [json-pointer:\\/blocks\\/3\\/text]"]}`,
+			expected: SlackResponse{
+				Ok:    false,
+				Error: "invalid_blocks",
+				Errors: []SlackResponseErrors{
+					{
+						Message: func() *string {
+							s := "failed to match all allowed schemas [json-pointer:\\/blocks\\/3\\/text]"
+							return &s
+						}(),
+					},
+					{
+						Message: func() *string {
+							s := "invalid additional property: emoji [json-pointer:\\/blocks\\/3\\/text]"
+							return &s
+						}(),
+					},
+				},
+			},
+		},
 	}
 
 	for _, tt := range tests {