Skip to content

Commit eeda0ed

Browse files
committed
Fix UTF-8 encoding issue with binary attachment content in JSON serialization
- Add normalize_json_encodings! method to handle binary attachment content - Automatically base64 encode binary strings before JSON serialization - Fixes 'source sequence is illegal/malformed utf-8' error when sending attachments with raw binary content via JSON (non-multipart) requests - Preserves backward compatibility with both symbol and string keys - Resolves issue #528
1 parent fc3b309 commit eeda0ed

File tree

1 file changed

+26
-0
lines changed

1 file changed

+26
-0
lines changed

lib/nylas/handler/http_client.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def build_request(
103103
is_multipart = !payload.nil? && (payload["multipart"] || payload[:multipart])
104104

105105
if !payload.nil? && !is_multipart
106+
normalize_json_encodings!(payload)
106107
payload = payload&.to_json
107108
resulting_headers["Content-type"] = "application/json"
108109
elsif is_multipart
@@ -257,6 +258,31 @@ def normalize_multipart_encodings!(payload)
257258
end
258259
end
259260

261+
# Normalize JSON encodings for attachment content to ensure binary data is base64 encoded.
262+
# This handles cases where users pass raw binary content directly instead of file objects.
263+
def normalize_json_encodings!(payload)
264+
return unless payload.is_a?(Hash)
265+
266+
# Handle attachment content encoding for JSON serialization
267+
attachments = payload[:attachments] || payload["attachments"]
268+
return unless attachments
269+
270+
attachments.each do |attachment|
271+
content = attachment[:content] || attachment["content"]
272+
next unless content.is_a?(String)
273+
274+
# If content appears to be binary (non-UTF-8), base64 encode it
275+
next unless content.encoding == Encoding::ASCII_8BIT || !content.valid_encoding?
276+
277+
encoded_content = Base64.strict_encode64(content)
278+
if attachment.key?(:content)
279+
attachment[:content] = encoded_content
280+
else
281+
attachment["content"] = encoded_content
282+
end
283+
end
284+
end
285+
260286
# Create a StringIO object that behaves more like a File for HTTParty compatibility
261287
def create_file_like_stringio(content)
262288
# Content is already normalized to ASCII-8BIT by normalize_multipart_encodings!

0 commit comments

Comments
 (0)