Skip to content

fix: interop forward built a double slash, so the product never registered - #181

Draft
benjamsf wants to merge 1 commit into
mainfrom
fix/interop-forward-double-slash
Draft

fix: interop forward built a double slash, so the product never registered#181
benjamsf wants to merge 1 commit into
mainfrom
fix/interop-forward-double-slash

Conversation

@benjamsf

Copy link
Copy Markdown
Member

Relates to the BattleLog proof-of-concept test. Do not merge without a maintainer's review — this touches every product integration call.

User-Facing Summary

  • POST /api/v1/product/interop/{tgt} answered 200 while the target product never received the request, so a product could never be registered for interop with another. In a live deployment BattleLog stayed permanently unregistered with matrixrmapi: every /interop/authz returned 403, and the room list, invite acceptance and Space search all failed with it.
  • Cause: views.py passed "/api/v1/interop/add" — a leading slash — and _method_to_product joins it as f"{productconf['api']}{url_suffix}". Every manifest api value ends in /, so the request went to https://matrix.<domain>:4626//api/v1/interop/add. yarl preserves the doubled slash and Starlette does not route it, so the product 404s.
  • That 404 becomes an aiohttp.ClientError, which _method_to_product swallows into None, and add_interop maps None to HTTP 200 with success: false — so the caller is told nothing useful.
  • This was the only call site in the codebase with a leading slash; descriptions, instructions, user CRUD and healthcheck all pass a relative suffix. That is exactly why interop alone failed while everything else worked over the same nginx, port and client certificate.

Why It's Good for the Product (Release Goal)

  • Product-to-product interop works at all; without it any product needing credentials from a sibling is silently dead.
  • The failure mode was the expensive kind: a success status hiding a total failure, three unrelated-looking symptoms in another product's UI, and nothing in the logs naming the cause.
  • The join now normalises both sides, so a future caller cannot reintroduce it. Behaviour-preserving for the existing relative call sites.

Verified

  • yarl 1.24.5 keeps the path as //api/v1/interop/add rather than collapsing it.
  • A mounted FastAPI app answers 200 for /api/v1/interop/add and 404 for //api/v1/interop/add.
  • Linters clean on both changed files. The full suite needs infrastructure I do not have locally, so CI is the check that matters here.

…tered

POST /api/v1/product/interop/{tgt} answered 200 while the target product never
saw the request. In a real deployment that left BattleLog permanently
unregistered with matrixrmapi: every /interop/authz came back 403, and the
product's room list, invite acceptance and Space search all failed with it.

views.py passed "/api/v1/interop/add" — a LEADING slash — into
post_to_product, and _method_to_product joins it as
f"{productconf['api']}{url_suffix}". Every manifest "api" value ends in "/"
(miniwerk manifests.py), so the result was

    https://matrix.<domain>:4626//api/v1/interop/add

yarl preserves the doubled slash and Starlette does not route it. Verified
directly: yarl 1.24.5 keeps the path as "//api/v1/interop/add", and a mounted
FastAPI app answers 200 for the single-slash path and 404 for the doubled one.
raise_for_status then turns that 404 into an aiohttp.ClientError, which
_method_to_product swallows into None, and add_interop maps None to
HTTP 200 with success:false — so the caller is told nothing useful.

That call site was the only one in the codebase with a leading slash; every
other product call (descriptions, instructions, user CRUD, healthcheck) passes
a relative suffix, which is exactly why interop alone failed while everything
else worked over the same nginx, port and client certificate.

Two changes: the suffix is now relative like all the others, and the join
normalises both sides so no future caller can reintroduce it. The second is
behaviour-preserving for the existing relative call sites.
benjamsf added a commit to pvarki/docker-rasenmaeher-integration that referenced this pull request Aug 29, 2026
Production confirmed the diagnosis from its own logs:

    Failure to call https://matrix.<domain>:4626//api/v1/interop/add:
    ClientResponseError(... status=404 ...)   productapihelpers.py:142

The doubled slash, a 404 from nginx, every five seconds. RM then turns that
into HTTP 200 with success:false, so BattleLog was told interop had been
granted while matrixrmapi had never heard of it — and answered every
/interop/authz with 403. The room list, invite acceptance and Space search all
failed with it.

pvarki/python-rasenmaeher-api#181 fixes the join. Until it merges and a release
tag exists, this pins the branch tag so a test server can actually run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant