Repository: praw-dev/praw

Issue:
Submitting images using `submit_image` fails with a 400 client error.
**Describe Bug**
Whenever the `submit_image` method is called on a subreddit object, I get a 400 client error. This has been going on for about a week and I was able to successfully submit images with it until March 3rd. Since then I have not been able to submit any images without getting a 400 client error. I had encountered some websocket timeouts but that was an error with the network on my end that I resolved.

**To Reproduce**
Steps to reproduce the behavior:
1. create `reddit` instance
2. run `subreddit.submit_image` on any subreddit

**Expected behavior**
The post to be submitted with the i.redd.it image service.

**System Info**
 - OS: MacOS 10.15 & Ubuntu 19.10
 - Python: 3.7.6 (mac) & 3.7.5 (ubuntu)
 - PRAW Version: 6.5.1 (both)



Discussion:
Upon further digging (packet sniffing) it looks like my image is just slightly too large for the i.redd.it service. It seems to be that reddit recently changed their upload policy with a max of a 20mb image file.
Thanks for mentioning that there is a file limit. I'll implement code that checks the file size to make sure that it doesn't exceed Reddit limits. Can you link where the policy change is?
@PythonCoderAS is the Reddit exception clear? If so, I'd prefer to simply rely on that than have a client-side check.
@bboe I would assume it isn't clear if a bug was filed. @ZucchiniZe what was the full exception? (Copy-paste text)

Edit: I can't easily access a dev environment with an authenticated Reddit instance, so I think the following code would work:

```python
from io import BytesIO
from praw.const import PNG_HEADER
from tempfile import NamedTemporaryFile

bio = BytesIO(PNG_HEADER + b"\x1a\x5a"*((1024**2)*10))
with NamedTemporaryFile(delete=False) as tempfile:
    tempfile.write(bio.read())

reddit.subreddit("test").submit_image("test", tempfile.name)
````

Just chiming in to say that client-side checks for server-side validation that may change on the server side is probably not a great strategy. It's inflexible and can get out of sync.
I have reproduced the error.

```python
HTTPError                                 Traceback (most recent call last)
<ipython-input-47-82fb8c6aa369> in <module>
      7     tempfile.write(bio.read())
      8
----> 9 reddit.subreddit("test").submit_image("test", tempfile.name)

/usr/local/lib/python3.7/site-packages/praw/models/reddit/subreddit.py in submit_image(self, title, image_path, flair_id, flair_text, resubmit, send_replies, nsfw, spoiler, timeout, collection_id, without_websockets)
    815         data.update(
    816             kind="image",
--> 817             url=self._upload_media(image_path, expected_mime_prefix="image"),
    818         )
    819         return self._submit_media(

/usr/local/lib/python3.7/site-packages/praw/models/reddit/subreddit.py in _upload_media(self, media_path, expected_mime_prefix)
    559                 upload_url, data=upload_data, files={"file": media}
    560             )
--> 561         response.raise_for_status()
    562
    563         return upload_url + "/" + upload_data["key"]

/usr/local/lib/python3.7/site-packages/requests/models.py in raise_for_status(self)
    938
    939         if http_error_msg:
--> 940             raise HTTPError(http_error_msg, response=self)
    941
    942     def close(self):

HTTPError: 400 Client Error: Bad Request for url: https://reddit-uploaded-media.s3-accelerate.amazonaws.com/
```
Confirmed that this does not occur if very small files are used. Those just throw a websocket error. 
Is there anything in the response body or headers that indicate image size?
I'm checking that next. I'm changing the code to print the response before error-ing. 
@bboe XML is returned. 
```xml
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>EntityTooLarge</Code>
<Message>Your proposed upload exceeds the maximum allowed size</Message><ProposedSize>20971528</ProposedSize>
<MaxSizeAllowed>20971520</MaxSizeAllowed><RequestId>23F056D6990D87E0</RequestId>
<HostId>iYEVOuRfbLiKwMgHt2ewqQRIm0NWL79uiC2rPLj9P0PwW554MhjY2/O8d9JdKTf1iwzLjwWMnGQ=</HostId>
</Error>
```

Resolving patch:
diff --git a/praw/exceptions.py b/praw/exceptions.py
--- a/praw/exceptions.py
+++ b/praw/exceptions.py
@@ -89,6 +89,25 @@ class MissingRequiredAttributeException(ClientException):
     """Indicate exceptions caused by not including a required attribute."""
 
 
+class TooLargeMediaException(ClientException):
+    """Indicate exceptions from uploading media that's too large."""
+
+    def __init__(self, maximum_size: int, actual: int):
+        """Initialize a TooLargeMediaException.
+
+        :param maximum_size: The maximum_size size of the uploaded media.
+        :param actual: The actual size of the uploaded media.
+        """
+        self.maximum_size = maximum_size
+        self.actual = actual
+        super().__init__(
+            "The media that you uploaded was too large (maximum size is "
+            "{maximum_size} bytes, uploaded {actual} bytes)".format(
+                maximum_size=maximum_size, actual=actual
+            )
+        )
+
+
 class WebSocketException(ClientException):
     """Indicate exceptions caused by use of WebSockets."""
 
diff --git a/praw/models/reddit/subreddit.py b/praw/models/reddit/subreddit.py
--- a/praw/models/reddit/subreddit.py
+++ b/praw/models/reddit/subreddit.py
@@ -6,6 +6,7 @@
 from json import dumps, loads
 from os.path import basename, dirname, join
 from urllib.parse import urljoin
+from xml.etree.ElementTree import XML
 
 import websocket
 from prawcore import Redirect
@@ -15,6 +16,7 @@
     APIException,
     ClientException,
     InvalidFlairTemplateID,
+    TooLargeMediaException,
     WebSocketException,
 )
 from ...util.cache import cachedproperty
@@ -515,6 +517,18 @@ def _fetch(self):
         self.__dict__.update(other.__dict__)
         self._fetched = True
 
+    def _parse_xml_response(self, response):
+        """Parse the XML from a response and raise any errors found."""
+        xml = response.text
+        root = XML(xml)
+        tags = [element.tag for element in root]
+        if tags[:4] == ["Code", "Message", "ProposedSize", "MaxSizeAllowed"]:
+            # Returned if image is too big
+            code, message, actual, maximum_size = [
+                element.text for element in root[:4]
+            ]
+            raise TooLargeMediaException(int(maximum_size), int(actual))
+
     def _submit_media(self, data, timeout, without_websockets):
         """Submit and return an `image`, `video`, or `videogif`.
 
@@ -615,6 +629,8 @@ def _upload_media(self, media_path, expected_mime_prefix=None):
             response = self._reddit._core._requestor._http.post(
                 upload_url, data=upload_data, files={"file": media}
             )
+        if not response.ok:
+            self._parse_xml_response(response)
         response.raise_for_status()
 
         return upload_url + "/" + upload_data["key"]


Test patch:
diff --git a/tests/integration/cassettes/TestSubreddit.test_submit_image__large.json b/tests/integration/cassettes/TestSubreddit.test_submit_image__large.json
new file mode 100644
--- /dev/null
+++ b/tests/integration/cassettes/TestSubreddit.test_submit_image__large.json
@@ -0,0 +1,226 @@
+{
+  "http_interactions": [
+    {
+      "recorded_at": "2020-03-14T12:46:38",
+      "request": {
+        "body": {
+          "encoding": "utf-8",
+          "string": "grant_type=password&password=<PASSWORD>&username=<USERNAME>"
+        },
+        "headers": {
+          "Accept": [
+            "*/*"
+          ],
+          "Accept-Encoding": [
+            "identity"
+          ],
+          "Authorization": [
+            "Basic <BASIC_AUTH>"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "Content-Length": [
+            "61"
+          ],
+          "Content-Type": [
+            "application/x-www-form-urlencoded"
+          ],
+          "User-Agent": [
+            "<USER_AGENT> PRAW/7.0.0.dev0 prawcore/1.0.1"
+          ]
+        },
+        "method": "POST",
+        "uri": "https://www.reddit.com/api/v1/access_token"
+      },
+      "response": {
+        "body": {
+          "encoding": "UTF-8",
+          "string": "{\"access_token\": \"<ACCESS_TOKEN>\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}"
+        },
+        "headers": {
+          "Accept-Ranges": [
+            "bytes"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "Content-Length": [
+            "118"
+          ],
+          "Content-Type": [
+            "application/json; charset=UTF-8"
+          ],
+          "Date": [
+            "Sat, 14 Mar 2020 12:46:38 GMT"
+          ],
+          "Server": [
+            "snooserv"
+          ],
+          "Set-Cookie": [
+            "edgebucket=LB4rz3ZkbuWNypf8W0; Domain=reddit.com; Max-Age=63071999; Path=/;  secure"
+          ],
+          "Strict-Transport-Security": [
+            "max-age=15552000; includeSubDomains; preload"
+          ],
+          "Via": [
+            "1.1 varnish"
+          ],
+          "X-Cache": [
+            "MISS"
+          ],
+          "X-Cache-Hits": [
+            "0"
+          ],
+          "X-Moose": [
+            "majestic"
+          ],
+          "X-Served-By": [
+            "cache-lga21927-LGA"
+          ],
+          "X-Timer": [
+            "S1584189998.761048,VS0,VE355"
+          ],
+          "cache-control": [
+            "max-age=0, must-revalidate"
+          ],
+          "x-content-type-options": [
+            "nosniff"
+          ],
+          "x-frame-options": [
+            "SAMEORIGIN"
+          ],
+          "x-xss-protection": [
+            "1; mode=block"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://www.reddit.com/api/v1/access_token"
+      }
+    },
+    {
+      "recorded_at": "2020-03-14T12:46:38",
+      "request": {
+        "body": {
+          "encoding": "utf-8",
+          "string": "api_type=json&filepath=tmpai97sm7x&mimetype=image%2Fjpeg"
+        },
+        "headers": {
+          "Accept": [
+            "*/*"
+          ],
+          "Accept-Encoding": [
+            "identity"
+          ],
+          "Authorization": [
+            "bearer <ACCESS_TOKEN>"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "Content-Length": [
+            "56"
+          ],
+          "Content-Type": [
+            "application/x-www-form-urlencoded"
+          ],
+          "Cookie": [
+            "edgebucket=LB4rz3ZkbuWNypf8W0"
+          ],
+          "User-Agent": [
+            "<USER_AGENT> PRAW/7.0.0.dev0 prawcore/1.0.1"
+          ]
+        },
+        "method": "POST",
+        "uri": "https://oauth.reddit.com/api/media/asset.json?raw_json=1"
+      },
+      "response": {
+        "body": {
+          "encoding": "UTF-8",
+          "string": "{\"args\": {\"action\": \"//reddit-uploaded-media.s3-accelerate.amazonaws.com\", \"fields\": [{\"name\": \"acl\", \"value\": \"public-read\"}, {\"name\": \"key\", \"value\": \"rte_images/4gjoa29pwmm41\"}, {\"name\": \"X-Amz-Credential\", \"value\": \"ASIATB45ILA562IO3THD/20200314/us-east-1/s3/aws4_request\"}, {\"name\": \"X-Amz-Algorithm\", \"value\": \"AWS4-HMAC-SHA256\"}, {\"name\": \"X-Amz-Date\", \"value\": \"20200314T124638Z\"}, {\"name\": \"success_action_status\", \"value\": \"201\"}, {\"name\": \"content-type\", \"value\": \"image/jpeg\"}, {\"name\": \"x-amz-storage-class\", \"value\": \"STANDARD\"}, {\"name\": \"x-amz-meta-ext\", \"value\": \"\"}, {\"name\": \"policy\", \"value\": \"eyJjb25kaXRpb25zIjogW3siYnVja2V0IjogInJlZGRpdC11cGxvYWRlZC1tZWRpYSJ9LCB7ImtleSI6ICJydGVfaW1hZ2VzLzRnam9hMjlwd21tNDEifSwgeyJhY2wiOiAicHVibGljLXJlYWQifSwgeyJ4LWFtei1zdG9yYWdlLWNsYXNzIjogIlNUQU5EQVJEIn0sIHsieC1hbXotY3JlZGVudGlhbCI6ICJBU0lBVEI0NUlMQTU2MklPM1RIRC8yMDIwMDMxNC91cy1lYXN0LTEvczMvYXdzNF9yZXF1ZXN0In0sIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwgeyJ4LWFtei1kYXRlIjogIjIwMjAwMzE0VDEyNDYzOFoifSwgeyJ4LWFtei1zZWN1cml0eS10b2tlbiI6ICJJUW9KYjNKcFoybHVYMlZqRUhVYUNYVnpMV1ZoYzNRdE1TSkhNRVVDSVFDQUlOTWRscjZvc2JtRytRSUkwZ0E3WmgybkJTM01oc1J5L2RKWHA4NVh5Z0lnUnpzUlBPdjZ2MEQwMnpqNmd3VVdhV1J1UVczSXNkYldlVWJzcGZlTzhNb3F0QU1JWGhBQUdnd3lNVEF5TkRZek1qWXpNekVpREY4R1JGSEFKV2ZVc2N0bHBTcVJBNDQwSEc3bEVsQjRmNlBPd2RtYjlmRHVmWTRSMUJwR1FMTFhMWXJFMHo2cnRVSjRQREc2bTZDTkhGc0RSN2QwQjJEN3NRYnlBUkcwU2g4Q1labEZOMDYxYTZVd2tNU1RtTHl3cFVhTTVwdGlkemFubDY0Z2pDQzBXZlNFZEQ2aFNOVCt1ODhvOGtqMGlBTjNFQ2RtVWdyVlFmbWx1ZWRqcE1OWlFqYlBMSUl2SHRyVkY5YVZNUVg1RGNDVTBsdmptdTBmNnIyRTV5NGpkQjg2NUdNeFBXUkJhRjRKbXJIRTdwSE1ZTHgwUlowQlZnbjk0YW1zdzBBRHY5RmxKTmFPWlJFOWlmdUdVQUNndVNxaUJodDN2emxWczNNelJIcWZkWkl5TXdVQ2JxK1A4M0xIODRiU2RIRlJ2WTBVMDJpOE9CVVhmTzVxaWg1ak9UZTdNeW5sZ1hOa2dBbitTaEZ5bVJQNXlrMFZpTWMyYnNBSTFEVlRXN1MwNFNJZE40TFd6L2tTczllcEphaFZLUVczMkRRVTkvOXI2ekFGVThINFZaR1FjejJJMFB5ekxsN0NONzJqc01KRk9IWGJDMGlQYmxlbFI2bWlzWHBSUDAwQlFGQmU3c1BkYTlobWZpWGdORnh0OXJrVVZCaitMU083c2czbGZhS0Z3SnVKNDNCeU5QaXg0MGIzUGJlQTQ0MkI4b3ArdVhwbE1McWlzL01GT3VzQlZVVFlNamY2d3RmTmNlT2lvcG9UUm5aOWV0VjFNbWd4cVgrbEFhV0hVb212eVR5ZmM1NG04N2pSMENxQVVISkx4Tm5UR05QUVZjanM5Z3ZMNjRWTll3TlBNb0tyVnQ2SmVEenI4aTFzN2dNNE13RytiejNYTXRIeVZ0SWJyaktDM0Zra0FDbHBQN2NsYm5Zc3dBRWg2V0tzeTBPdlRhYnJnVGx1bTBIWVl5YkNPanJ0V3BtYnd1Y0ZTK2VtOGpTMVlJOENPbXdNZmJ1d2NYTGI0Skw3T0lPRVZVOUdNckNVaEErckYvMXZST1VENkpDK28zTk4yY2R6NFhhdFpxekgzcnlMb2RjakVtS2F4YlU4UHZLK2RNckdKZWRFL3JrelYyRjhmY3RVNjZZbWlIaStKaUMzSU5yOXB3PT0ifSwgeyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiAiMjAxIn0sIFsiY29udGVudC1sZW5ndGgtcmFuZ2UiLCAwLCAyMDk3MTUyMF0sIHsieC1hbXotbWV0YS1leHQiOiAiIn0sIHsiY29udGVudC10eXBlIjogImltYWdlL2pwZWcifV0sICJleHBpcmF0aW9uIjogIjIwMjAtMDMtMTRUMTI6NDc6MzhaIn0=\"}, {\"name\": \"X-Amz-Signature\", \"value\": \"fbc2b7b3cb800313265c08db97b8e94fd1710e928e036ed54b1b46a1ae00468b\"}, {\"name\": \"x-amz-security-token\", \"value\": \"IQoJb3JpZ2luX2VjEHUaCXVzLWVhc3QtMSJHMEUCIQCAINMdlr6osbmG+QII0gA7Zh2nBS3MhsRy/dJXp85XygIgRzsRPOv6v0D02zj6gwUWaWRuQW3IsdbWeUbspfeO8MoqtAMIXhAAGgwyMTAyNDYzMjYzMzEiDF8GRFHAJWfUsctlpSqRA440HG7lElB4f6POwdmb9fDufY4R1BpGQLLXLYrE0z6rtUJ4PDG6m6CNHFsDR7d0B2D7sQbyARG0Sh8CYZlFN061a6UwkMSTmLywpUaM5ptidzanl64gjCC0WfSEdD6hSNT+u88o8kj0iAN3ECdmUgrVQfmluedjpMNZQjbPLIIvHtrVF9aVMQX5DcCU0lvjmu0f6r2E5y4jdB865GMxPWRBaF4JmrHE7pHMYLx0RZ0BVgn94amsw0ADv9FlJNaOZRE9ifuGUACguSqiBht3vzlVs3MzRHqfdZIyMwUCbq+P83LH84bSdHFRvY0U02i8OBUXfO5qih5jOTe7MynlgXNkgAn+ShFymRP5yk0ViMc2bsAI1DVTW7S04SIdN4LWz/kSs9epJahVKQW32DQU9/9r6zAFU8H4VZGQcz2I0PyzLl7CN72jsMJFOHXbC0iPblelR6misXpRP00BQFBe7sPda9hmfiXgNFxt9rkUVBj+LSO7sg3lfaKFwJuJ43ByNPix40b3PbeA442B8op+uXplMLqis/MFOusBVUTYMjf6wtfNceOiopoTRnZ9etV1MmgxqX+lAaWHUomvyTyfc54m87jR0CqAUHJLxNnTGNPQVcjs9gvL64VNYwNPMoKrVt6JeDzr8i1s7gM4MwG+bz3XMtHyVtIbrjKC3FkkAClpP7clbnYswAEh6WKsy0OvTabrgTlum0HYYybCOjrtWpmbwucFS+em8jS1YI8COmwMfbuwcXLb4JL7OIOEVU9GMrCUhA+rF/1vROUD6JC+o3NN2cdz4XatZqzH3ryLodcjEmKaxbU8PvK+dMrGJedE/rkzV2F8fctU66YmiHi+JiC3INr9pw==\"}]}, \"asset\": {\"asset_id\": \"4gjoa29pwmm41\", \"processing_state\": \"incomplete\", \"payload\": {\"filepath\": \"tmpai97sm7x\"}, \"websocket_url\": \"wss://ws-0f6ba0a7a86f40302.wss.redditmedia.com/rte_images/4gjoa29pwmm41?m=AQAAhtRsXuggNwzxp_bWoFZjlqlkSz-ntb7Vett7Nze9oWI9P2HL\"}}"
+        },
+        "headers": {
+          "Accept-Ranges": [
+            "bytes"
+          ],
+          "Connection": [
+            "keep-alive"
+          ],
+          "Content-Length": [
+            "4145"
+          ],
+          "Content-Type": [
+            "application/json; charset=UTF-8"
+          ],
+          "Date": [
+            "Sat, 14 Mar 2020 12:46:38 GMT"
+          ],
+          "Server": [
+            "snooserv"
+          ],
+          "Strict-Transport-Security": [
+            "max-age=15552000; includeSubDomains; preload"
+          ],
+          "Vary": [
+            "accept-encoding"
+          ],
+          "Via": [
+            "1.1 varnish"
+          ],
+          "X-Cache": [
+            "MISS"
+          ],
+          "X-Cache-Hits": [
+            "0"
+          ],
+          "X-Moose": [
+            "majestic"
+          ],
+          "X-Served-By": [
+            "cache-lga21957-LGA"
+          ],
+          "X-Timer": [
+            "S1584189998.404923,VS0,VE58"
+          ],
+          "cache-control": [
+            "private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate"
+          ],
+          "expires": [
+            "-1"
+          ],
+          "set-cookie": [
+            "loid=00000000005bn5wlvy.2.1577584524387.Z0FBQUFBQmViTkl1NU5mbFE3RUZ0SE5Bb01PelVaa1BmVXBvWkV3UUNKTkl0aGNCUWhqUXE5My1seXI4Q041YWlBVk5GeHJOeWlFTzZJUXctSDIxZGNvQ2Fyb0Y3aDdGaFlqMGpiZGpscnhEV3lNZnluZGRDbFlMbmxVV1F0Smp3emtqcmhfYk1tS3M; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Mon, 14-Mar-2022 12:46:38 GMT; secure",
+            "session_tracker=UTMgmmQufRoMkYb3YA.0.1584189998442.Z0FBQUFBQmViTkl1NjFrb0gxTWNoMnlXWVhGemV1cVUtaVJyOXpnNDlfZEI4ejB0dlM0RXpGaDZBUDFpUUhPdkwyb1ZuQzNNOW4wak50OVd6VnI1cXBEQmVvQ1l6X2hvN0FRUGtlS0tCTUxIYTJVMHNSZFQwTkZvdUtzYU5VZi1RMlRYbDFNZEwyZEY; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sat, 14-Mar-2020 14:46:38 GMT; secure"
+          ],
+          "x-content-type-options": [
+            "nosniff"
+          ],
+          "x-frame-options": [
+            "SAMEORIGIN"
+          ],
+          "x-ratelimit-remaining": [
+            "595.0"
+          ],
+          "x-ratelimit-reset": [
+            "202"
+          ],
+          "x-ratelimit-used": [
+            "5"
+          ],
+          "x-xss-protection": [
+            "1; mode=block"
+          ]
+        },
+        "status": {
+          "code": 200,
+          "message": "OK"
+        },
+        "url": "https://oauth.reddit.com/api/media/asset.json?raw_json=1"
+      }
+    }
+  ],
+  "recorded_with": "betamax/0.8.1"
+}
\ No newline at end of file
diff --git a/tests/integration/models/reddit/test_subreddit.py b/tests/integration/models/reddit/test_subreddit.py
--- a/tests/integration/models/reddit/test_subreddit.py
+++ b/tests/integration/models/reddit/test_subreddit.py
@@ -6,13 +6,16 @@
 
 import mock
 import pytest
+import requests
 import websocket
 from prawcore import Forbidden, NotFound, RequestException, TooLarge
 
+from praw.const import PNG_HEADER
 from praw.exceptions import (
     APIException,
     ClientException,
     InvalidFlairTemplateID,
+    TooLargeMediaException,
     WebSocketException,
 )
 from praw.models import (
@@ -344,6 +347,47 @@ def test_submit_image(self, _, __):
                 assert submission.is_reddit_media_domain
                 assert submission.title == "Test Title"
 
+    @mock.patch("time.sleep", return_value=None)
+    def test_submit_image__large(self, _, tmp_path):
+        reddit = self.reddit
+        reddit.read_only = False
+        mock_data = (
+            '<?xml version="1.0" encoding="UTF-8"?>'
+            "<Error>"
+            "<Code>EntityTooLarge</Code>"
+            "<Message>Your proposed upload exceeds the maximum "
+            "allowed size</Message>"
+            "<ProposedSize>20971528</ProposedSize>"
+            "<MaxSizeAllowed>20971520</MaxSizeAllowed>"
+            "<RequestId>23F056D6990D87E0</RequestId>"
+            "<HostId>iYEVOuRfbLiKwMgHt2ewqQRIm0NWL79uiC2rPLj9P0PwW55"
+            "4MhjY2/O8d9JdKTf1iwzLjwWMnGQ=</HostId>"
+            "</Error>"
+        )
+        _post = reddit._core._requestor._http.post
+
+        def patch_request(url, *args, **kwargs):
+            """Patch requests to return mock data on specific url."""
+            if (
+                "https://reddit-uploaded-media.s3-accelerate.amazonaws.com"
+                in url
+            ):
+                response = requests.Response()
+                response._content = mock_data.encode("utf-8")
+                response.status_code = 400
+                return response
+            return _post(url, *args, **kwargs)
+
+        reddit._core._requestor._http.post = patch_request
+        fake_png = PNG_HEADER + b"\x1a" * 10  # Normally 1024 ** 2 * 20 (20 MB)
+        with open(tmp_path.joinpath("fake_img.png"), "wb") as tempfile:
+            tempfile.write(fake_png)
+        with self.recorder.use_cassette(
+            "TestSubreddit.test_submit_image__large"
+        ):
+            with pytest.raises(TooLargeMediaException):
+                reddit.subreddit("test").submit_image("test", tempfile.name)
+
     @mock.patch("time.sleep", return_value=None)
     @mock.patch("websocket.create_connection", return_value=WebsocketMock())
     def test_submit_image__bad_websocket(self, _, __):