Skip to content

Django backend retains automatically opened client after send failure #38

Closed

Description

@Kayvan-Zahiri

Describe the bug

EmailBackend.send_messages closes a client it creates only when every batch succeeds. If send_batch raises, or a response contains a per-message failure, the backend retains the open client even when the caller did not explicitly open it. This affects both fail_silently settings.

To reproduce

With Django settings configured locally:

from unittest.mock import Mock, patch
from django.core.mail import EmailMessage
from postmark.django import EmailBackend

client = Mock()
client.outbound.send_batch.side_effect = RuntimeError("local failure")
with patch("postmark.django.backend.SyncServerClient", return_value=client):
    backend = EmailBackend(server_token="local-test", fail_silently=True)
    backend.send_messages([
        EmailMessage("example", "body", "sender@example.com", ["to@example.com"])
    ])
    assert client.close.called  # false
    assert backend.client is None  # false

Expected behavior

Close automatically created clients on failure as well as success. Preserve an explicitly opened/caller-owned connection for reuse. Keep exception and fail_silently behavior unchanged.

Environment

  • postmark-python 0.4.0 / current main
  • Python 3.12, Django 5.2, macOS

Additional context

The send loop and close call can be wrapped in try/finally inside the existing exception-handling boundary. I plan to cover transport errors and API response failures, both ownership modes and both fail_silently settings. Local mocked transport only; no actual emails sent. AI assistance: OpenAI Codex.

Activity

  1. gregsvo commented on Oct 28, 2026

    @gregsvo
    Collaborator

    Hi @Kayvan-Zahiri ! Thank you for such a clear report. Your reproduction was exactly what I needed... and I confirmed on main that a client the backend opens itself is left attached after a failed send, especially important in the in the common 1send_mail()1 path... every call builds a new backend, so I appreciate you ending the leaking HTTP client(s).
    Your fix in #39 looks like the right one. I'm reviewing it there now and I'll follow up on that PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions