Repository: getmoto/moto (python)

Issue:
[DynamoDb] All Errors are deserialized as DynamoDbException in JAVA SDK
Using MotoServer to test Java based services. This Service creates the DynamoDb table if it doesn't exist, ie, on ResourceNotFoundException, table is created.
When using the MotoServer, We get response

```json
{"__type": "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException", "message": "Requested resource not found"}
```
When this is deserialized in java sdk, as there is no error-code for  `com.amazonaws.dynamodb.v20111205#ResourceNotFoundException` hence, All the error is wrapped, and returned as `DynamoDbException`

With other services, similar issue is not occurring. The issue exists for version>=3.1.5, and seems to be introduced by this https://github.com/getmoto/moto/pull/5012
Tried using <=3.1.4 to overcome this problem, but ended up encountering another bug related to Glue, which is fixed in the recent version. So, unable to use the older versions as a workaround

Could you please share reason behind the error message? would it be possible to have error message compatible with Java SDKs too?


Pull request:
DynamoDB: describe_table() - return correct error msg
Fixes #5993 

The `com.amazonaws.dynamodb.v20111205#`-prefix was a holdover from the old DynamoDB, in use with boto2. All error types from the current DynamoDB version seem to be prefixed with `com.amazonaws.dynamodb.v20120810#` instead.
This also changes the error message in the body - `describe_table()` returns a slightly different error message than other methods.

Patch:
diff --git a/moto/dynamodb/exceptions.py b/moto/dynamodb/exceptions.py
index 87642779a..017c35b7c 100644
--- a/moto/dynamodb/exceptions.py
+++ b/moto/dynamodb/exceptions.py
@@ -4,12 +4,15 @@ from moto.core.exceptions import JsonRESTError
 from moto.dynamodb.limits import HASH_KEY_MAX_LENGTH, RANGE_KEY_MAX_LENGTH
 
 
+ERROR_TYPE_PREFIX = "com.amazonaws.dynamodb.v20120810#"
+
+
 class DynamodbException(JsonRESTError):
     pass
 
 
 class MockValidationException(DynamodbException):
-    error_type = "com.amazonaws.dynamodb.v20111205#ValidationException"
+    error_type = ERROR_TYPE_PREFIX + "ValidationException"
 
     def __init__(self, message: str):
         super().__init__(MockValidationException.error_type, message=message)
@@ -190,7 +193,7 @@ class IncorrectDataType(MockValidationException):
 
 
 class ConditionalCheckFailed(DynamodbException):
-    error_type = "com.amazonaws.dynamodb.v20111205#ConditionalCheckFailedException"
+    error_type = ERROR_TYPE_PREFIX + "ConditionalCheckFailedException"
 
     def __init__(self, msg: Optional[str] = None):
         super().__init__(
@@ -279,44 +282,47 @@ class TooManyAddClauses(InvalidUpdateExpression):
 
 
 class ResourceNotFoundException(JsonRESTError):
-    def __init__(self, msg: Optional[str] = None):
-        err = "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException"
-        super().__init__(err, msg or "Requested resource not found")
+    def __init__(self, msg: Optional[str] = None, table_name: Optional[str] = None):
+        err = ERROR_TYPE_PREFIX + "ResourceNotFoundException"
+        default_msg = "Requested resource not found"
+        if table_name is not None:
+            default_msg += f": Table: {table_name} not found"
+        super().__init__(err, msg or default_msg)
 
 
 class TableNotFoundException(JsonRESTError):
     def __init__(self, name: str):
-        err = "com.amazonaws.dynamodb.v20111205#TableNotFoundException"
+        err = ERROR_TYPE_PREFIX + "TableNotFoundException"
         super().__init__(err, f"Table not found: {name}")
 
 
 class SourceTableNotFoundException(JsonRESTError):
     def __init__(self, source_table_name: str):
-        er = "com.amazonaws.dynamodb.v20111205#SourceTableNotFoundException"
+        er = ERROR_TYPE_PREFIX + "SourceTableNotFoundException"
         super().__init__(er, f"Source table not found: {source_table_name}")
 
 
 class BackupNotFoundException(JsonRESTError):
     def __init__(self, backup_arn: str):
-        er = "com.amazonaws.dynamodb.v20111205#BackupNotFoundException"
+        er = ERROR_TYPE_PREFIX + "BackupNotFoundException"
         super().__init__(er, f"Backup not found: {backup_arn}")
 
 
 class TableAlreadyExistsException(JsonRESTError):
     def __init__(self, target_table_name: str):
-        er = "com.amazonaws.dynamodb.v20111205#TableAlreadyExistsException"
+        er = ERROR_TYPE_PREFIX + "TableAlreadyExistsException"
         super().__init__(er, f"Table already exists: {target_table_name}")
 
 
 class ResourceInUseException(JsonRESTError):
     def __init__(self) -> None:
-        er = "com.amazonaws.dynamodb.v20111205#ResourceInUseException"
+        er = ERROR_TYPE_PREFIX + "ResourceInUseException"
         super().__init__(er, "Resource in use")
 
 
 class StreamAlreadyEnabledException(JsonRESTError):
     def __init__(self) -> None:
-        er = "com.amazonaws.dynamodb.v20111205#ResourceInUseException"
+        er = ERROR_TYPE_PREFIX + "ResourceInUseException"
         super().__init__(er, "Cannot enable stream")
 
 
diff --git a/moto/dynamodb/models/__init__.py b/moto/dynamodb/models/__init__.py
index 7b03cb1ba..333b1ed63 100644
--- a/moto/dynamodb/models/__init__.py
+++ b/moto/dynamodb/models/__init__.py
@@ -123,8 +123,10 @@ class DynamoDBBackend(BaseBackend):
         return tables, None
 
     def describe_table(self, name: str) -> Dict[str, Any]:
-        table = self.get_table(name)
-        return table.describe(base_key="Table")
+        # We can't use get_table() here, because the error message is slightly different for this operation
+        if name not in self.tables:
+            raise ResourceNotFoundException(table_name=name)
+        return self.tables[name].describe(base_key="Table")
 
     def update_table(
         self,


Test patch:
diff --git a/tests/test_dynamodb/test_dynamodb.py b/tests/test_dynamodb/test_dynamodb.py
index 2146184dc..d7b19810c 100644
--- a/tests/test_dynamodb/test_dynamodb.py
+++ b/tests/test_dynamodb/test_dynamodb.py
@@ -66,7 +66,9 @@ def test_describe_missing_table_boto3():
         conn.describe_table(TableName="messages")
     ex.value.response["Error"]["Code"].should.equal("ResourceNotFoundException")
     ex.value.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
-    ex.value.response["Error"]["Message"].should.equal("Requested resource not found")
+    ex.value.response["Error"]["Message"].should.equal(
+        "Requested resource not found: Table: messages not found"
+    )
 
 
 @mock_dynamodb
diff --git a/tests/test_dynamodb/test_server.py b/tests/test_dynamodb/test_server.py
index 9afa08076..ae6ddc88f 100644
--- a/tests/test_dynamodb/test_server.py
+++ b/tests/test_dynamodb/test_server.py
@@ -1,5 +1,6 @@
+import json
 import sure  # noqa # pylint: disable=unused-import
-
+from moto import mock_dynamodb
 import moto.server as server
 
 """
@@ -7,6 +8,7 @@ Test the different server responses
 """
 
 
+@mock_dynamodb
 def test_table_list():
     backend = server.create_backend_app("dynamodb")
     test_client = backend.test_client()
@@ -17,3 +19,18 @@ def test_table_list():
     res = test_client.get("/", headers=headers)
     res.data.should.contain(b"TableNames")
     res.headers.should.have.key("X-Amz-Crc32")
+
+    headers = {"X-Amz-Target": "DynamoDB_20120810.DescribeTable"}
+    res = test_client.post(
+        "/", headers=headers, data=json.dumps({"TableName": "test-table2"})
+    )
+    res.headers.should.have.key("X-Amzn-ErrorType").equals(
+        "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException"
+    )
+    body = json.loads(res.data.decode("utf-8"))
+    body.should.equal(
+        {
+            "__type": "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException",
+            "message": "Requested resource not found: Table: test-table2 not found",
+        }
+    )