Skip to content

Release 0.36.2 - #72

Merged
odlbot merged 16 commits into
releasefrom
release-candidate
Sep 15, 2026
Merged

odlbot merged 16 commits into
releasefrom
release-candidate

Conversation

@odlbot

@odlbot odlbot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Anastasia Beglova

Matt Bertrand

Tobias Macey

renovate[bot]

renovate Bot and others added 16 commits September 4, 2026 11:31
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Production learn-ai pods show continuous "Unclosed client session"
asyncio warnings and per-pod memory climbing from ~558MB to 900MB+
before replacement, alongside readiness-probe failures and APISIX p95
latency spiking to 7.5-24s. litellm's aiohttp transport
(LiteLLMAiohttpTransport._get_valid_client_session in
litellm/llms/custom_httpx/aiohttp_transport.py, litellm==1.89.3) caches
one ClientSession per event loop and, when it detects the cached
session belongs to a different/closed loop than the current one
(routine across Channels' per-consumer async contexts), schedules the
old session's close() as a fire-and-forget task or, if that can't be
scheduled, abandons it for GC per the library's own code comment. That
GC fallback is what produces the unclosed-session warnings and leaks
the underlying sockets.

Set litellm.disable_aiohttp_transport = True at app startup so async
LLM calls fall back to httpx, which this app already pools and closes
correctly via ai_chatbots.utils.HTTPClientManager. litellm's own
comment claims aiohttp gives higher throughput than httpx, but that
tradeoff isn't worth the active production leak.


Claude-Session: https://claude.ai/code/session_017H4oTvJX9jqEixKmsTLWmp

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…TAIL rows (#62)

* fix(sentry): cap request bodies at 1KB and scrub Postgres DETAIL rows

Two ways learner data reaches Sentry, neither gated by send_default_pii.

Request bodies. The SDK sets request.data unconditionally at
sentry_sdk/integrations/_wsgi_common.py:123; max_request_body_size,
checked at :61, is the only control, and left unset it defaults to
"medium" -- 10,000-byte bodies. Nobody chose that. Measured over the
last 30 days, the write endpoints that actually raise are the sensitive
ones: SCIM user PATCH, /api/v1/enrollments/, /api/checkout/result/,
/api/checkout/redeem_discount/, /api/profile/details/, and CMS page
edits. Set to "small" explicitly, so the choice is findable at the call
site instead of in a dependency's defaults.

Postgres DETAIL lines. A constraint violation carries a DETAIL line that
echoes the whole offending row, and psycopg puts it in str(exc) -- so it
ships inside the exception value, which no SDK privacy option covers.
Measured on mitxonline MITXONLINE-6PK: a SCIM PATCH IntegrityError
reproducing a learner email address three times per event, 46,764
occurrences since 2026-05-27. before_send now truncates at the DETAIL
marker across exception values, logentry, and the legacy top-level
message, keeping the primary error that names the failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RVJKTGk9KHUTN2xfU59ujX

* fix(sentry): scrub DETAIL rows from the whole event, not three named fields

Copilot review, verified against sentry-sdk 2.55.0 source. The first pass
enumerated three paths -- exception values, logentry.message/.formatted,
and the legacy top-level message -- and missed every other field that can
carry the same string:

  breadcrumbs[].message   LoggingIntegration records each log record as a
                          breadcrumb (integrations/logging.py:311). This is
                          exactly MITXONLINE-6PK's shape: mechanism=logging,
                          logger=django_scim.views.
  logentry.params         record.args verbatim (:274), so
                          logger.error("...: %s", exc) carries it.
  frames[].vars           include_local_variables defaults to True
                          (consts.py:1028, utils.py:616), so a catch block
                          holding the exception in a local carries it.

Confirmed the old implementation leaked on all three shapes before
changing it; the new tests fail against it and pass against the walk.

Replaced with a recursive walk of the event instead of a longer path
list -- it covers these without enumerating them and does not go stale
when the SDK grows another such field. The walk only rewrites str leaves
and preserves everything else, with a test pinning that.

Copilot also suggested normalizing exception-valued params. Not needed:
client._prepare_event serializes the event before calling before_send
(client.py:650 vs :658), so every leaf is already a JSON primitive by
then and there are no live exception objects left to coerce.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RVJKTGk9KHUTN2xfU59ujX

* fix(sentry): scrub repr'd DETAIL lines and narrow the body-size comment

The SDK repr()s frame locals and non-string logging params while
serializing, so there the DETAIL line arrives with a literal backslash-n
and the "\nDETAIL:" marker missed it. Match both forms, and test through
the real SDK so the hand-built events can't pass while the real path leaks.

The max_request_body_size comment described mitxonline payloads. Here it
only reaches the regular Django views, not the Channels chatbot endpoints,
and under ASGI a request with no Content-Length bypasses the bound.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYc2F3cFvSCfVzqeRMshGy

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@odlbot
odlbot requested a review from a team as a code owner September 14, 2026 19:59
@odlbot
odlbot merged commit 23d59bf into release Sep 15, 2026
5 checks passed
@odlbot
odlbot deleted the release-candidate branch September 15, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants