Skip to content

AttributeError: 'Error_OpaqueWithText' object has no attribute 'message' #1789

@dinager

Description

@dinager

Bug Report: AttributeError in aws-cryptographic-material-providers

Summary

The AWS Cryptographic Material Providers library crashes with an AttributeError when deserializing KMS errors, preventing proper error handling and masking the underlying AWS KMS permission issue.

Error Details

  • Exception Type: AttributeError
  • Exception Message: 'Error_OpaqueWithText' object has no attribute 'message'
  • Library: aws-cryptographic-material-providers
  • File: smithygenerated/aws_cryptography_materialproviders/deserialize.py
  • Line: 375

Root Cause

The code at line 375 attempts to access .message attribute on error.ComAmazonawsKms, but when the KMS error is an Error_OpaqueWithText type, it has .objMessage instead of .message.

Source Location:

Problematic Code (Line 375)

elif error.is_ComAmazonawsKms:
    return ComAmazonawsKms(message=_dafny.string_of(error.ComAmazonawsKms.message))

Actual Error Structure

Error_ComAmazonawsKms(
    ComAmazonawsKms=Error_OpaqueWithText(
        obj=ClientError(...),
        objMessage=<_dafny.Seq object>  # Note: objMessage, not message
    )
)

Stack Trace

/usr/local/lib/python3.11/site-packages/aws_encryption_sdk/__init__.py:218 in decrypt
│   215 │   │   self._set_config_kwargs("decrypt", kwargs)
│   216 │   │   kwargs["signature_policy"] = SignaturePolicy.ALLOW_ENCRYPT_ALL
│   217 │   │   with StreamDecryptor(**kwargs) as decryptor:
│ ❱ 218 │   │   │   plaintext = decryptor.read()
│   219 │   │   return plaintext, decryptor.header

/usr/local/lib/python3.11/site-packages/aws_encryption_sdk/streaming_client.py:342 in read
│    339 │   │   output = io.BytesIO()
│    340 │   │
│    341 │   │   if not self._message_prepped:
│ ❱  342 │   │   │   self._prep_message()
│    343 │   │

/usr/local/lib/python3.11/site-packages/aws_encryption_sdk/streaming_client.py:941 in _prep_message
│    938 │
│    939 │   def _prep_message(self):
│    940 │   │   """Performs initial message setup."""
│ ❱  941 │   │   self._header, self.header_auth = self._read_header()

/usr/local/lib/python3.11/site-packages/aws_encryption_sdk/streaming_client.py:1045 in _read_header
│   1042 │   │   │   )
│   1043 │   │
│   1044 │   │   decrypt_materials_request = self._create_decrypt_materials_request(...)
│ ❱ 1045 │   │   decryption_materials = self.config.materials_manager.decrypt_materials(...)

/usr/local/lib/python3.11/site-packages/aws_encryption_sdk/materials_managers/mpl/cmm.py:124 in decrypt_materials
│   121 │   │   try:
│   122 │   │   │   mpl_input: 'MPL_DecryptMaterialsInput' = \
│   123 │   │   │   │   CryptoMaterialsManagerFromMPL._create_mpl_decrypt_materials_input(...)
│ ❱ 124 │   │   │   mpl_output: 'MPL_DecryptMaterialsOutput' = self.mpl_cmm.decrypt_materials(...)

/usr/local/lib/python3.11/site-packages/aws_cryptographic_material_providers/smithygenerated/aws_cryptography_materialproviders/references.py:566 in decrypt_materials
│   563 │   │   │   │   _deserialize_error as aws_cryptography_materialproviders_deserialize_error
│   564 │   │   │   )
│   565 │   │   │
│ ❱ 566 │   │   │   raise aws_cryptography_materialproviders_deserialize_error(
│   567 │   │   │   │   dafny_output.error
│   568 │   │   │   )

/usr/local/lib/python3.11/site-packages/aws_cryptographic_material_providers/smithygenerated/aws_cryptography_materialproviders/deserialize.py:330 in _deserialize_error
│   327 │   elif error.is_CollectionOfErrors:
│   328 │   │   return CollectionOfErrors(
│   329 │   │   │   message=_dafny.string_of(error.message),
│ ❱ 330 │   │   │   list=[_deserialize_error(dafny_e) for dafny_e in error.list],
│   331 │   │   )

/usr/local/lib/python3.11/site-packages/aws_cryptographic_material_providers/smithygenerated/aws_cryptography_materialproviders/deserialize.py:330 in <listcomp>
│   327 │   elif error.is_CollectionOfErrors:
│   328 │   │   return CollectionOfErrors(
│   329 │   │   │   message=_dafny.string_of(error.message),
│ ❱ 330 │   │   │   list=[_deserialize_error(dafny_e) for dafny_e in error.list],
│   331 │   │   )

╭───────────────────────────────── locals ─────────────────────────────────╮
│      .0 = <iterator object at 0x...>                                     │
│ dafny_e = Error_ComAmazonawsKms(                                         │
│           │   ComAmazonawsKms=Error_OpaqueWithText(                      │
│           │   │   obj=ClientError('An error occurred                     │
│           (AccessDeniedException) when calling the Decrypt operation:    │
│           User: arn:aws:iam::<ACCOUNT_ID>:user/<USERNAME> is             │
│           not authorized to perform: kms:Decrypt on the resource         │
│           associated with this ciphertext because the resource does not  │
│           exist in this Region, no resource-based policies allow access, │
│           or a resource-based policy explicitly denies access'),         │
│           │   │   objMessage=<_dafny.Seq object at 0x...>                │
│           │   ),                                                         │
│           )                                                              │
╰──────────────────────────────────────────────────────────────────────────╯

/usr/local/lib/python3.11/site-packages/aws_cryptographic_material_providers/smithygenerated/aws_cryptography_materialproviders/deserialize.py:375 in _deserialize_error
│   372 │   │   │   aws_cryptography_keystore_deserialize_error(error.AwsCryptographyKeyStore)
│   373 │   │   )
│   374 │   elif error.is_ComAmazonawsKms:
│ ❱ 375 │   │   return ComAmazonawsKms(message=_dafny.string_of(error.ComAmazonawsKms.message))
│   376 │   elif error.is_ComAmazonawsDynamodb:
│   377 │   │   return ComAmazonawsDynamodb(
│   378 │   │   │   message=_dafny.string_of(error.ComAmazonawsDynamodb.message)

Expected Behavior

When a KMS decryption error occurs (e.g., AccessDeniedException), the library should properly deserialize and propagate the error with a clear message about the underlying AWS KMS issue.

Actual Behavior

The error deserialization crashes with AttributeError, masking the actual KMS permission error and making debugging difficult.

Proposed Fix

The code should recursively deserialize nested errors, similar to how other error types are handled (lines 364-372):

elif error.is_ComAmazonawsKms:
    return ComAmazonawsKms(_deserialize_error(error.ComAmazonawsKms))
elif error.is_ComAmazonawsDynamodb:
    return ComAmazonawsDynamodb(_deserialize_error(error.ComAmazonawsDynamodb))

This would properly handle cases where the nested error is an Error_OpaqueWithText or any other error type.

Environment

  • Python: 3.11
  • aws-encryption-sdk==4.0.3
  • aws-cryptographic-material-providers==1.11.1

Reproducibility

This occurs when:

  1. Attempting to decrypt data encrypted with AWS KMS
  2. The KMS service returns an error (e.g., AccessDeniedException)
  3. The error is wrapped as Error_OpaqueWithText instead of having a direct .message attribute

Impact

Severity: Medium-High

This bug prevents proper error handling and makes debugging KMS permission issues extremely difficult, as the original KMS error message is lost in the secondary AttributeError.

Notes

This appears to be machine-generated code (file header indicates "This file is machine generated, and any changes to it will be overwritten"). The fix should be applied to the code generation template.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions