Repository: getmoto/moto

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?



Discussion:
Thanks for raising this @munendrasn - marking it as a bug.

#5012 was an attempt to streamline all error messages - but because we only fully test against the Python SDK, which is more forgiving, we never realized it was broken. Will raise a PR shortly.

Patch:
diff --git a/moto/dynamodb/exceptions.py b/moto/dynamodb/exceptions.py
--- a/moto/dynamodb/exceptions.py
+++ b/moto/dynamodb/exceptions.py
@@ -4,12 +4,15 @@
 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 @@ def __init__(self) -> None:
 
 
 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 @@ def __init__(self) -> None:
 
 
 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
--- a/moto/dynamodb/models/__init__.py
+++ b/moto/dynamodb/models/__init__.py
@@ -123,8 +123,10 @@ def list_tables(
         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
--- 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
--- 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 @@
 """
 
 
+@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",
+        }
+    )