diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py
index 17da1236d..f359e5e96 100644
--- a/moto/s3/exceptions.py
+++ b/moto/s3/exceptions.py
@@ -6,6 +6,10 @@ ERROR_WITH_BUCKET_NAME = """{% extends 'error' %}
{% block extra %}{{ bucket }}{% endblock %}
"""
+ERROR_WITH_KEY_NAME = """{% extends 'error' %}
+{% block extra %}{{ key_name }}{% endblock %}
+"""
+
class S3ClientError(RESTError):
pass
@@ -40,6 +44,17 @@ class MissingBucket(BucketError):
*args, **kwargs)
+class MissingKey(S3ClientError):
+ code = 404
+
+ def __init__(self, key_name):
+ super(MissingKey, self).__init__(
+ "NoSuchKey",
+ "The specified key does not exist.",
+ Key=key_name,
+ )
+
+
class InvalidPartOrder(S3ClientError):
code = 400
diff --git a/moto/s3/models.py b/moto/s3/models.py
index d4506678b..685254eba 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -11,7 +11,7 @@ import six
from bisect import insort
from moto.core import BaseBackend
from moto.core.utils import iso_8601_datetime_with_milliseconds, rfc_1123_datetime
-from .exceptions import BucketAlreadyExists, MissingBucket, InvalidPart, EntityTooSmall
+from .exceptions import BucketAlreadyExists, MissingBucket, MissingKey, InvalidPart, EntityTooSmall
from .utils import clean_key_name, _VersionedKeyStore
UPLOAD_ID_BYTES = 43
@@ -393,11 +393,13 @@ class S3Backend(BaseBackend):
bucket = self.get_bucket(bucket_name)
if bucket:
if version_id is None:
- return bucket.keys.get(key_name)
+ if key_name in bucket.keys:
+ return bucket.keys[key_name]
else:
for key in bucket.keys.getlist(key_name):
if str(key._version_id) == str(version_id):
return key
+ raise MissingKey(key_name=key_name)
def initiate_multipart(self, bucket_name, key_name, metadata):
bucket = self.get_bucket(bucket_name)
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index 78ffd99b6..3c1d82bce 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -399,11 +399,8 @@ class ResponseObject(_TemplateEnvironmentMixin):
template = self.response_template(S3_OBJECT_ACL_RESPONSE)
return 200, headers, template.render(obj=key)
- if key:
- headers.update(key.metadata)
- return 200, headers, key.value
- else:
- return 404, headers, ""
+ headers.update(key.metadata)
+ return 200, headers, key.value
def _key_response_put(self, request, body, bucket_name, query, key_name, headers):
if query.get('uploadId') and query.get('partNumber'):