Repository: getmoto/moto
Issue category: Bug Report

Value must be a nonempty dictionary whose key is a valid dynamodb type
The following code fails with moto:

```python
import boto3
from moto import mock_aws

with mock_aws():
    dynamodb = boto3.resource(
        "dynamodb", region_name="us-east-1"
    )
    table = dynamodb.create_table(
        TableName="dummy",
        AttributeDefinitions=[
            {"AttributeName": "client", "AttributeType": "S"},
            {"AttributeName": "app", "AttributeType": "S"},
        ],
        KeySchema=[
            {"AttributeName": "client", "KeyType": "HASH"},
            {"AttributeName": "app", "KeyType": "RANGE"},
        ],
        ProvisionedThroughput={"ReadCapacityUnits": 123, "WriteCapacityUnits": 123},
    )
    table.update_item(
        Key={"client": "clientA", "app": "appB"},
        UpdateExpression=(
            "SET " "#items = list_append(:new_items, if_not_exists(#items, :empty))"
        ),
        ExpressionAttributeValues={
            ":empty": [],
            ":new_items": [
                b"\n\x014\x18\xc3\xb0\xf8\xba\x06",
                b"\n\x012\x18\xc3\xb0\xf8\xba\x06",
            ],
        },
        ExpressionAttributeNames={"#items": "items"},
        ReturnValues="UPDATED_NEW",
    )
```

Here is the error:
```
Traceback (most recent call last):
  File "<stdin>", line 17, in <module>
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/resources/factory.py", line 581, in do_action
    response = action(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/resources/action.py", line 88, in __call__
    response = getattr(parent.meta.client, operation_name)(*args, **params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/botocore/client.py", line 565, in _api_call
    return self._make_api_call(operation_name, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/botocore/client.py", line 1003, in _make_api_call
    self.meta.events.emit(
  File "/home/denis/test/env/lib/python3.12/site-packages/botocore/hooks.py", line 412, in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/botocore/hooks.py", line 256, in emit
    return self._emit(event_name, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/botocore/hooks.py", line 239, in _emit
    response = handler(**kwargs)
               ^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/transform.py", line 227, in inject_attribute_value_output
    self._transformer.transform(
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/transform.py", line 289, in transform
    self._transform_parameters(model, params, transformation, target_shape)
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/transform.py", line 296, in _transform_parameters
    getattr(self, f'_transform_{type_name}')(
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/transform.py", line 312, in _transform_structure
    self._transform_parameters(
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/transform.py", line 296, in _transform_parameters
    getattr(self, f'_transform_{type_name}')(
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/transform.py", line 326, in _transform_map
    params[key] = transformation(value)
                  ^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/types.py", line 279, in deserialize
    return deserializer(value[dynamodb_type])
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/types.py", line 306, in _deserialize_l
    return [self.deserialize(v) for v in value]
            ^^^^^^^^^^^^^^^^^^^
  File "/home/denis/test/env/lib/python3.12/site-packages/boto3/dynamodb/types.py", line 268, in deserialize
    raise TypeError(
TypeError: Value must be a nonempty dictionary whose key is a valid dynamodb type.
```

When I include the `endpoint_url` parameter to point to the locally running DynamoDB container:

```python
dynamodb = boto3.resource(
    "dynamodb",
    region_name="us-east-1",
    endpoint_url="http://localhost:8000"
)
```

It starts working. However, it seems that in this scenario, AWS mocking is bypassed.
```
{'Attributes': {'items': [Binary(b'\n\x014\x18\xc3\xb0\xf8\xba\x06'), Binary(b'\n\x012\x18\xc3\xb0\xf8\xba\x06')]}, 'ResponseMetadata': {'RequestId': '2f870e67-18fc-4d69-829e-4e96fabd6460', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'Jetty(12.0.8)', 'date': 'Sun, 15 Dec 2024 00:36:44 GMT', 'x-amzn-requestid': '2f870e67-18fc-4d69-829e-4e96fabd6460', 'content-type': 'application/x-amz-json-1.0', 'x-amz-crc32': '163102387', 'content-length': '74'}, 'RetryAttempts': 0}}
```


```
moto==5.0.22
``` 



Functions to edit:
moto/dynamodb/models/utilities.py:DynamoJsonEncoder.default

Patch:
diff --git a/moto/dynamodb/models/utilities.py b/moto/dynamodb/models/utilities.py
index 6d4636e787a4..52b97efc4be5 100644
--- a/moto/dynamodb/models/utilities.py
+++ b/moto/dynamodb/models/utilities.py
@@ -1,3 +1,4 @@
+import base64
 import json
 import re
 from typing import Any, Dict, List, Optional
@@ -7,6 +8,8 @@ class DynamoJsonEncoder(json.JSONEncoder):
     def default(self, o: Any) -> Any:
         if hasattr(o, "to_json"):
             return o.to_json()
+        elif isinstance(o, bytes):
+            return base64.b64encode(o).decode("utf-8")
 
 
 def dynamo_json_dump(dynamo_object: Any) -> str:

Test patch: diff --git a/tests/test_dynamodb/test_dynamodb.py b/tests/test_dynamodb/test_dynamodb.py
index f8b930289ec4..1905c4e8c4fb 100644
--- a/tests/test_dynamodb/test_dynamodb.py
+++ b/tests/test_dynamodb/test_dynamodb.py
@@ -4952,3 +4952,25 @@ def test_query_with_gsi_reverse_paginated():
         {"pri": {"S": "pri_10"}, "alt": {"S": "alt_2"}},
     ]
     assert "LastEvaluatedKey" not in p2
+
+
+@pytest.mark.aws_verified
+@dynamodb_aws_verified()
+def test_update_item_with_list_of_bytes(table_name=None):
+    dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
+    table = dynamodb.Table(table_name)
+
+    b1 = b"\n\x014\x18\xc3\xb0\xf8\xba\x06"
+    b2 = b"\n\x012\x18\xc3\xb0\xf8\xba\x06"
+
+    update = table.update_item(
+        Key={"pk": "clientA"},
+        UpdateExpression="SET #items = :new_items",
+        ExpressionAttributeValues={":new_items": [b1, b2]},
+        ExpressionAttributeNames={"#items": "items"},
+        ReturnValues="UPDATED_NEW",
+    )
+    assert update["Attributes"]["items"] == [Binary(b1), Binary(b2)]
+
+    get = table.get_item(Key={"pk": "clientA"})
+    assert get["Item"] == {"pk": "clientA", "items": [Binary(b1), Binary(b2)]}