prevent 500 error on cyclic space hierarchies #2036

Merged
ginger merged 2 commits from Reaster/continuwuity:self-contained-room into main 2026-07-25 13:55:30 +00:00
Contributor

Requesting GET /_matrix/client/v1/rooms/{roomId}/hierarchy on a space whose graph contains a cycle — for example a space that
includes itself as a child, or two spaces that reference each other — fails with:

500 Internal Server Error
{"errcode":"M_UNKNOWN","error":"Space hierarchy is unreasonably large"}

This makes the space impossible to open in clients such as Element.

get_room_hierarchy_for_user in src/service/rooms/summary/mod.rs traverses the space tree depth-first using a stack of layers
(queue), but the only deduplication set it keeps is inaccessible_children. There is no visited-set for accessible rooms, so when
the graph contains a cycle, the same room is fetched and its children pushed again on every revisit. Each revisit adds a new
layer, so the traversal depth grows without bound.

Two factors then turn this into a 500:

  • The client route (src/api/client/space.rs) only applies a depth limit when the client explicitly sends max_depth. Element does
    not, so max_depth stays None and the depth guard never fires.
  • The traversal eventually trips the hardcoded queue.len() > 50 backstop, which returns a bare-string error that maps to
    M_UNKNOWN / HTTP 500.

Additionally, even when max_depth is provided, a cycle currently produces duplicate room entries in the response, which violates
the spec: MSC2946 requires each room to appear at most once.

Changes

  • src/service/rooms/summary/mod.rs: add a visited set, seeded with the root room, and skip any room already present in the
    response. This terminates cycles naturally and guarantees room uniqueness as required by the spec.
  • src/api/client/space.rs: default max_depth to MAX_MAX_DEPTH (10) when the client omits it, so the traversal is always
    depth-bounded.
  • The queue.len() > 50 backstop is kept for genuinely enormous hierarchies, but now returns 413 M_TOO_LARGE instead of a 500,
    since it is a property of the request target rather than a server fault.
  • Added a changelog entry (changelog.d/space-hierarchy-cycle.bugfix.md).

The federation hierarchy endpoint (/_matrix/federation/v1/hierarchy) is unaffected: it only summarizes a single level and does
not use this traversal.

Requesting GET /_matrix/client/v1/rooms/{roomId}/hierarchy on a space whose graph contains a cycle — for example a space that includes itself as a child, or two spaces that reference each other — fails with: 500 Internal Server Error {"errcode":"M_UNKNOWN","error":"Space hierarchy is unreasonably large"} This makes the space impossible to open in clients such as Element. get_room_hierarchy_for_user in src/service/rooms/summary/mod.rs traverses the space tree depth-first using a stack of layers (queue), but the only deduplication set it keeps is inaccessible_children. There is no visited-set for accessible rooms, so when the graph contains a cycle, the same room is fetched and its children pushed again on every revisit. Each revisit adds a new layer, so the traversal depth grows without bound. Two factors then turn this into a 500: - The client route (src/api/client/space.rs) only applies a depth limit when the client explicitly sends max_depth. Element does not, so max_depth stays None and the depth guard never fires. - The traversal eventually trips the hardcoded queue.len() > 50 backstop, which returns a bare-string error that maps to M_UNKNOWN / HTTP 500. Additionally, even when max_depth is provided, a cycle currently produces duplicate room entries in the response, which violates the spec: MSC2946 requires each room to appear at most once. Changes - src/service/rooms/summary/mod.rs: add a visited set, seeded with the root room, and skip any room already present in the response. This terminates cycles naturally and guarantees room uniqueness as required by the spec. - src/api/client/space.rs: default max_depth to MAX_MAX_DEPTH (10) when the client omits it, so the traversal is always depth-bounded. - The queue.len() > 50 backstop is kept for genuinely enormous hierarchies, but now returns 413 M_TOO_LARGE instead of a 500, since it is a property of the request target rather than a server fault. - Added a changelog entry (changelog.d/space-hierarchy-cycle.bugfix.md). The federation hierarchy endpoint (/_matrix/federation/v1/hierarchy) is unaffected: it only summarizes a single level and does not use this traversal.
deduplicate rooms in hierarchy traversal
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Auto Labeler / Apply labels based on changed files (pull_request_target) Successful in 3s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Checks / Prek / Check changed files (pull_request) Successful in 5s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 3m2s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 6m37s
965d861365
Author
Contributor

my deadass really just went "now what happend if i try to put the same space into itself?" and pouf space bricked wonderfully haha

my deadass really just went "now what happend if i try to put the same space into itself?" and pouf space bricked wonderfully haha
Owner

🤨 I was under the impression that it was both legal and expected for a space to appear multiple times in the hierarchy output if it exists multiple times in the room graph

🤨 I was under the impression that it was both legal and expected for a space to appear multiple times in the hierarchy output if it exists multiple times in the room graph
Author
Contributor

i don't know how it could possibly work to have the same space twice in a same space three without causing any recursion error when showing this three

i don't know how it could possibly work to have the same space twice in a same space three without causing any recursion error when showing this three
Author
Contributor

i think theres no problem to see the same space appear multiple time in a same space "group" at different "branch" but if it happend more than one on the same "branch" (aka: at one point space Y contain space Y under itself) theres no way to not have a recursion issue

i think theres no problem to see the same space appear multiple time in a same space "group" at different "branch" but if it happend more than one on the same "branch" (aka: at one point space Y contain space Y under itself) theres no way to not have a recursion issue
Owner

I think this is an acceptable change for now to at least keep recursive space hierarchies from becoming unrecoverable

I think this is an acceptable change for now to at least keep recursive space hierarchies from becoming unrecoverable
Owner

This currently fails clippy, I'll approve it once that's fixed

This currently fails clippy, I'll approve it once that's fixed
make clippy happy again
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Checks / Prek / Check changed files (pull_request) Successful in 5s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m20s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 8m51s
e1726d8733
Author
Contributor

done, it was just a one-liner issue

done, it was just a one-liner issue
Reaster force-pushed self-contained-room from e1726d8733
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Checks / Prek / Check changed files (pull_request) Successful in 5s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m20s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 8m51s
to fc0b960622
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 5s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 56s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m10s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 8m35s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
2026-07-23 15:07:25 +00:00
Compare
ginger approved these changes 2026-07-25 13:55:24 +00:00
ginger merged commit a838d59cfc into main 2026-07-25 13:55:30 +00:00
ginger added this to the 26.7.0 milestone 2026-07-25 13:55:34 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
continuwuation/continuwuity!2036
No description provided.