Skip to content

fix(matrix): surface oversized inbound media - #125637

Draft
iainlane wants to merge 163 commits into
NousResearch:mainfrom
iainlane:fix/matrix-oversized-media
Draft

iainlane wants to merge 163 commits into
NousResearch:mainfrom
iainlane:fix/matrix-oversized-media

Conversation

@iainlane

@iainlane iainlane commented Sep 27, 2026 •

Copy link
Copy Markdown

Stack

All PRs in this section target main. Nesting records logical merge order. A parent must merge before its child. Linked descriptions list any additional prerequisites.

Oversized Matrix attachments reach the conversation as a text marker with their caption and filename. The adapter uses the smaller Matrix and gateway limit. It stops streamed downloads when that limit is exceeded, including media with omitted or inaccurate size metadata.

Quoted images use the same bounded download path. The quoted-image consumer passes the required limit, so accepted images remain available in reply context.

Luyifan authored the original feature. All nine original feature and follow-up commits preserve the full author and committer identities, dates, timezones, messages and order. This update composes those records on the verified media prerequisite without changing their source or assertions.

Review scope

Merge the prerequisites in the Stack section first. Review this nine-record layer with the scoped comparison. The 154 signed prerequisite records remain unchanged. Only the nine commits in this layer have new signatures.

Verification

The canonical runner passes 702 cases across five complete gateway files, with four workers, no retries and strict resource, runtime, unraisable-exception and unhandled-thread warnings. All 9237 tracked Python, configuration, lock and runner inputs match the exact candidate before and after execution.

Three complete files with 288 cases are resource-clean. The complete effective-event-state file (180 cases) and turn-context file (234 cases) emit 28 and 254 database finalization warnings after passing, respectively. Both complete files reproduce exactly the same warnings on the original published PR head. Those results are qualified; the 702-case result is not resource-clean.

A fresh native run passes all three cases in the complete media-caption module, including declared oversized metadata and an omitted-size stream against a ten-byte gateway cap. A separate Matrix client sends each event through a disposable homeserver and the real gateway. The model receives the caption and filename marker without a cached oversized attachment. The accepted caption and cached-file path also pass on this candidate. All setup, call and teardown phases pass without resource warnings, retries or skips. The native run binds 8427 actual Python and runner inputs, with package and lock inputs bound separately to its newly built package-managed image.

Ruff passes across all nine Python paths in this layer. The full type comparison reports 92 diagnostics on the original, 90 on the prerequisite and 91 on the candidate. Every candidate diagnostic matches a complete original or prerequisite occurrence; there are no introduced diagnostics. The same nine inherited formatter paths remain. Committed whitespace checks pass.

The exact candidate merges cleanly with the fixed campaign validation base, 3637c512fc3211bdccb61b7140d09ac1018508ba. These are local results; hosted CI execution and maintainer approval remain separate.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/plugins Plugin system and bundled plugins platform/matrix Matrix adapter (E2EE) area/docker sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation labels Sep 27, 2026
Matrix permalinks change with each triggering event. Rendering one in
the pinned session prompt made the prompt stale or changed its bytes
every turn, which breaks conversation prompt caching.

Put the link in the current model-facing user message and remove it from
the durable transcript, including queued turns. Respect effective PII
redaction when adding the note. Percent-encode room IDs, event IDs and
the via server so delimiters cannot change the matrix.to URL structure.

Preserve the Matrix and Discord source-note handling when integrating
the upstream prefix removal change. Use removeprefix after selecting
the platform-specific note, so each transport removes only its own
generated prefix.
The live Matrix exchange now checks the model request for a permalink to
the event sent by the independent client. It also verifies that the link
is absent from the system prompt, which must remain stable across turns.
Record the exchange duration separately from container setup time.
Room version 12 IDs have no server component. A permalink built only
from the room ID therefore omitted the server hint, which can prevent a
client outside the room from locating it.

Use the joined bot account's server when the room ID has no domain. The
builder still leaves via absent when neither server is known.
A room ID can identify a server that no longer participates in the
room. Its server may also include a port or IPv6 address, which the
last-colon parser truncated to an invalid via hint.

Use the joined bot account's server for inbound source links. When only
a room ID is available, parse its full server after the first colon.
The same parse also corrects the room's session scope.
In a thread, every turn's source link pointed at the thread root. The
root was chosen as a stable anchor while the link lived in the pinned
session prompt, which is per thread. The link now goes on each user
turn, so the reason no longer applies, and the model never received the
ID of the message that triggered the turn. A request to cite "this
message" produced a link to the root, which may be another user's
message.

Build the per-turn link from the triggering event in all cases. A
matrix.to link to an event inside a thread opens that thread in current
clients, and the session prompt already gives the thread root.
A room-ID permalink needs via servers so that a reader outside the room
can join through one of them. The link carried only the bot's server.
When that server was unreachable from the reader's server, the link
failed.

Choose up to three servers as the Matrix specification recommends for
room-ID permalinks. The first is the server of the highest-power joined
member at power level 50 or above; room version 12 creators count as
the highest power. The rest are the servers with the most joined
members, and IP literals are never chosen. The adapter reads members,
power levels and the create event from the mautrix state store, which
sync keeps current, so building a link makes no request. When the
store has no joined members for the room, use the bot's server and then
the room ID's server.

Server ACLs are not applied, because the state store does not keep
m.room.server_acl. Move the builder and the selection into a sibling
module, and emit one via parameter per server.
The session prompt stopped rendering the Matrix source link when the
link moved to the current user turn. Two prompt tests still asserted
that the prompt omitted the link when it was unset or redacted. No code
path produces that output any more, so neither test could fail because
of this feature. The prompt-stability test covers the remaining
contract, and the per-turn tests cover the note and its PII
suppression.

Restore the comment on the per-turn notes. strip_inbound_source_note
removes a note only when it is the first thing in the message, so a
later prefix added outside the notes would leave the note in the
persisted transcript.
Choosing source-link via servers made every inbound message await
get_power_levels and get_create on the client's state store. The
project-context isolation and voice tests gave the adapter a MagicMock
store with only get_members awaitable, so all six inbound tests in
those files failed with "'MagicMock' object can't be awaited".

Production always uses mautrix's MemoryStateStore. Give both fakes a
real MemoryStateStore in which the bot and the sender have joined the
test rooms. The real store implements every state query, so these tests
keep working when the adapter starts reading other room state. The
stores are filled with MemoryStateStore.deserialize because the helpers
are synchronous and some are called inside a running event loop.
The effective-state parent added a description to the matrix_read limit
argument: "Maximum number of events to read". In this layer the same
argument also bounds the members kind, which returns members rather
than events, and the pins kind. Say that the limit applies to events or
members so the model can tell that it bounds member lists too.
The merged matrix_read description said that events are listed oldest
first and that `errors` lists events that could not be decrypted. Both
sentences came from the history reads. A pins read returns events in
the order of the room's m.room.pinned_events list, and its errors also
cover pins that could not be fetched, are not visible messages, came
back as another event, or whose edit was redacted. A model that read
the description could tell a user that a deleted pin was an encrypted
message.

Limit the ordering sentence to room and thread reads, say that pins
follow the room's pinned order, and say that `errors` lists events that
could not be read or decrypted, with the reason. History reads already
returned non-decryption errors such as "event content changed", so the
wider wording is also accurate for them.
Room classification controls the Matrix room allowlist and mention
gates.
The previous member-count shortcut classified a bot-only room as a DM,
and an m.direct entry classified larger or unreadable rooms as DMs.
Those rooms could bypass both gates.

Classify a room as a private bot chat only when the bot and exactly one
other user are joined. Request JOIN membership explicitly: mautrix's
state store includes invited users by default. The state store may also
contain only a subset of a room's members, so use its joined IDs only
when has_full_member_list confirms completeness. Otherwise query the
homeserver with get_joined_members and apply room rules if that fails.

Keep m.direct as room identity metadata, without using it for
authorisation. Document the resulting allowlist and mention policy.

Tests reproduced the bot-only, bot-absent, multi-user, invited-user,
partial-cache and unknown-membership cases before their fixes.
The DM tests marked m.direct account data but did not supply joined
members. The two-person classifier therefore treated those fixtures
as rooms, so their threading assertions tested the wrong policy.

Give the fixture a full joined member list with the bot and one other
user. Keep the three-member variant for room behaviour. The mention
and threading tests now exercise the intended DM policy.
A room is a private bot chat only when the bot's user ID is one of its
two joined members. With an access token, the adapter replaces the
configured MATRIX_USER_ID with the ID that whoami returns. After a
password login it kept the configured string. Synapse accepts a login
identifier whose localpart differs in case from the registered user, so
a configured @Hermes:example.org logs in as @Hermes:example.org. The
membership check then never matched, and every one-to-one chat followed
room rules: with MATRIX_ALLOWED_ROOMS set its messages were dropped,
and otherwise they needed a mention.

mautrix's login stores the homeserver's user ID in client.mxid. Copy it
into the adapter after a successful login, as the whoami path does, so
the membership check uses the ID that appears in the room's member
list.
The Matrix handoff session-key test marked its DM case only through the
m.direct cache. Room classification no longer reads m.direct, so the
DM case was keyed as a group and the test failed. Give the fake client
a joined member list: the bot and one other user for the DM case, and a
third user for the room case.
The environment-variable reference still said that any room with two or
fewer joined members is a DM, and told operators to add a third member
to get room behaviour. A bot-only room, a room without the bot and a
room whose membership cannot be read now follow room rules. Describe
the current rule in the MATRIX_ALLOWED_ROOMS, MATRIX_REQUIRE_MENTION,
MATRIX_FREE_RESPONSE_ROOMS and MATRIX_AUTO_THREAD rows.

The comment in _schedule_pending_invite_joins said that a direct invite
missing from m.direct is misclassified as a group. m.direct no longer
affects classification, so drop that clause.
The E2EE crypto store keys every row (the Olm account, Olm sessions
and inbound Megolm sessions) by an account ID taken from the adapter's
user ID. After a password login the adapter now replaces the configured
MATRIX_USER_ID with the homeserver's spelling of it, so the DM check
matches the room's member list. That replacement also changed the store
key. A deployment with a password login, MATRIX_USER_ID differing in
case from the registered user, and a stable MATRIX_DEVICE_ID would open
an empty store after upgrading. It would create a new Olm account for
its existing device and could no longer decrypt history. The same
happened on an in-process reconnect, because the second login read the
replaced ID.

Keep the configured ID in its own attribute and use it for the login
identifier and, after a password login, for the store key, as before.
The access-token path keys the store by the whoami answer, as it always
has. The adapter's user ID still becomes the homeserver's spelling on
both paths, and the DM, mention, invite and self-sender checks use it.

Preserve the HTTP API supplied to the password-login fixture client and
disconnect its first real connection before reconnecting. The fixture
keeps every configured-account and canonical-identity assertion. The
shared Client constructor preserves the supplied API for this fixture.
Matrix rooms often have no m.room.name state event. Identity
resolution used the opaque room ID as the name shown to the agent
even when other occupants had readable display names.

Read member profiles from the state store, with the joined members
API as a fallback when the store is empty. Use the other occupants
to title an unnamed room. Explicit-name detection and DM
classification remain unchanged.
Matrix room identity was cached without reacting to live state
changes. Topic edits, renames and membership changes could leave
stale room context. The agent also missed important privacy or
replacement changes.

Register sync-awaited state handlers that invalidate the identity
cache. Queue one note per change kind for the next ordinary message.
Keep notes through mention drops, commands and text batching. Quote
and bound room-state values before they enter channel context.
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 8, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 8, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 8, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 8, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 8, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 8, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
The fake HTTP API discarded the client_session supplied by the Matrix
adapter, so disconnect closed a mock while the real aiohttp session was
left open. A reconnect test also replaced disconnect with a stub and
left its new connection open after checking the guard. Strict resource
warnings expose both leaks.

Preserve the supplied session in the fake API. Use a wrapping disconnect
spy in the reconnect test and disconnect the replacement client after
the existing call assertion. The fake API and reconnect fixture predate
this branch, so this focused fixture maintenance is separate from the
corrections folded into their campaign contributors. Production code
and all original assertions are unchanged.

On the earlier Matrix prerequisite, prebuilt Client stubs also discarded
the supplied API. Use the same scoped constructor for the inherited
fixtures so disconnect closes the adapter-created session. This is the
earlier fixture layout; all original assertions remain unchanged.
Matrix requests can outlive gateway teardown. A stopped but open owner
loop accepts a threadsafe coroutine, yet cannot run its cancellation
callback. The tool's deadline then waits indefinitely for the owner to
drain. A result may also be complete before the stopped loop copies it
back to the worker.

Reject unavailable loops before admission and monitor loop availability
while waiting for both the result and cancellation. Keep a confirmed
owner-task result. Report an unknown outcome if a write was dispatched
before loop loss, and return a normal unavailable-loop error otherwise.
Cancellation remains queued for the owner and sets the existing interrupt
flag, so an operation cannot resume into a new write after recovery.

A real separate-loop contract reproduces four failures before this change
and preserves the confirmed-result control. All five cases and the
adjacent administration and pin tests pass, 264 tests in four files.
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 9, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 10, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
@iainlane
iainlane force-pushed the fix/matrix-oversized-media branch from 5ed5beb to 46c24f0 Compare October 10, 2026 03:18
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 10, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 10, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 10, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 10, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
iainlane added a commit to iainlane/hermes-agent that referenced this pull request Oct 11, 2026
When the Matrix adapter could not download or cache an inbound
attachment for a reason other than its size, such as a network error,
an empty download or bytes that the image cache rejects, it gave the
agent the attachment's `/_matrix/client/v1/media/download/...` URL
instead of a cached file. That endpoint requires the bot's access
token, so the agent's attempts to fetch the URL failed, and the model
could repeat the URL in its reply. Encrypted media had no URL to fall
back to: the event kept its media message type with no attachment, and
nothing told the agent that the user had sent one.

When the download or cache step produces no file, the adapter now
sends a text event with the user's caption followed by a marker such
as `[matrix image attachment could not be downloaded: photo.png]`. The
marker has the same shape as the `[matrix <kind> attachment too large:
<filename>]` marker proposed in NousResearch#125637, and `_inbound_media_marker`
builds it so that the oversized-media path can use the same helper.
The filename is the declared `filename`, or a body that contains only
the transport filename once any reply fallback is removed. The event
has no media URL or media type, so downstream tools do not treat the
attachment as downloaded.

`_mxc_to_http` has no other caller and is removed.

Integration: use the shared filename rule and preserve the canonical
sticker kind, retained reply context, mention claim and admission result.
Oversize failures still take precedence. The current SDK never returns
None, so the file failure fixture uses the subsequent source timeout
correction at the streamed download boundary. The affected gate passes
319 tests; four original failure cases were reproduced before the fix.

(cherry picked from commit cdc5ce6)
@alt-glitch alt-glitch added area/install-update Installer, updater, packaging, wheels, doctor and removed area/docker labels Oct 17, 2026

This branch has not been deployed

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

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists platform/matrix Matrix adapter (E2EE) sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants