Skip to content

Conversation

@viren-nadkarni
Copy link
Member

@viren-nadkarni viren-nadkarni commented Sep 11, 2025

Background

A common pattern in LocalStack providers is to import Moto module and invoke backend methods directly:

from moto.ses import ses_backends

@handler("SendRawEmail")
def send_raw_email(context, ...) -> SendRawEmailResponse:
    backend = ses_backend[context.account_id][context.region_name]
    ...
    message = backend.send_raw_email(source, destinations, raw_data)

The problem here is that any exception raised by Moto is not understood by the ASF serialiser. This causes it to return a HTTP 5xx with an error log containing the trace:

exception while calling ses.SendRawEmail: Traceback (most recent call last):
...
  File "/opt/code/localstack/.venv/lib/python3.10/site-packages/localstack/services/ses/provider.py", line 455, in send_raw_email
    message = backend.send_raw_email(source, destinations, raw_data)
  File "/opt/code/localstack/.venv/lib/python3.10/site-packages/moto/ses/models.py", line 339, in send_raw_email
    raise MessageRejectedError(
moto.ses.exceptions.MessageRejectedError: 400 Bad Request: <?xml version="1.0" encoding="UTF-8"?>
  <ErrorResponse>
    <Errors>
      <Error>
        <Code>MessageRejected</Code>
        <Message><![CDATA[Did not have authority to send from email [email protected]]]></Message>
      </Error>
    </Errors>
  <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ErrorResponse>

Changes

This PR introduces a context manager that can be used to wrap such Moto invocations. It can translate Moto exceptions into ASF serialisable exceptions, which propagates the proper 4xx error code.

This context manager currently only supports ServiceException. Some Moto services use RESTError. Adding support for this is left for future iteration.

This PR also deploys this new context manager to an SES handler usage.

An alternative to this context manager could have been integrating this into ASF. I have no strong preference about this. It seemed better to keep this decoupled from ASF due to the changing landscape in Moto.

Tests

Includes unit tests for the context manager, plus an AWS-verified integration test for the SES change.

Related

Closes PNX-104

@viren-nadkarni viren-nadkarni self-assigned this Sep 11, 2025
@viren-nadkarni viren-nadkarni added semver: patch Non-breaking changes which can be included in patch releases docs: skip Pull request does not require documentation changes labels Sep 11, 2025
@github-actions
Copy link

Test Results - Preflight, Unit

22 125 tests   20 387 ✅  6m 18s ⏱️
     1 suites   1 738 💤
     1 files         0 ❌

Results for commit 70cf161.

@github-actions
Copy link

Test Results (amd64) - Acceptance

7 tests   5 ✅  3m 5s ⏱️
1 suites  2 💤
1 files    0 ❌

Results for commit 70cf161.

@github-actions
Copy link

Test Results (amd64) - Integration, Bootstrap

    5 files      5 suites   2h 37m 24s ⏱️
5 030 tests 4 539 ✅ 491 💤 0 ❌
5 036 runs  4 539 ✅ 497 💤 0 ❌

Results for commit 70cf161.

@github-actions
Copy link

LocalStack Community integration with Pro

    2 files      2 suites   1h 59m 8s ⏱️
4 656 tests 4 325 ✅ 331 💤 0 ❌
4 658 runs  4 325 ✅ 333 💤 0 ❌

Results for commit 70cf161.

Copy link
Member

@alexrashed alexrashed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nice implementation of the exception handling as a context manager which can easily be reused! 💯
I wonder if it would be possible to even further generalize the exception handling for services which have been migrated to moto's new core AWS serializer... 🤔
That's definitely something we can iterate on, nothing blocking a merge here!

Comment on lines +481 to +482
with translate_service_exception:
message = backend.send_raw_email(source, destinations, raw_data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Nice, this is a really clean, nice, and reusable way to apply the exception handling! I am just wondering if there is a way to maybe have a generic way to wrap all backend operation invocations in a way?
For example by using a decorator on the whole object? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Having a decorator for handler methods is great idea. I think it may be even better to integrate this in the ASF serialiser which will hide this complexity from provider level. I'll experiment with this in a follow up iteration.

import moto.backends as moto_backends
from moto.core.base_backend import BackendDict
from moto.core.exceptions import RESTError
from moto.core.exceptions import RESTError, ServiceException
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Is the ServiceException always being raised for issues like this (and only for issues like this), and would it be raised to the handler chain level? Otherwise there might be a chance to tackle this globally in the exception handler chain in app.py?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is only raised when importing and invoking Moto methods, not with FallbackDispatcher or call_moto(). For the latter, the serialised error response is returned from Moto, which can easily be passed down.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! So it's only relevant for direct moto backend invocations in our service provider implementations.
Do you think this is done often enough such that it should be handled globally?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll investigate this in the follow-up PR

@viren-nadkarni viren-nadkarni merged commit 515c19b into main Sep 12, 2025
65 of 67 checks passed
@viren-nadkarni viren-nadkarni deleted the moto-exc-translator branch September 12, 2025 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs: skip Pull request does not require documentation changes semver: patch Non-breaking changes which can be included in patch releases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants