Files
platform/todo/plugin-infrastructure/a-plugin-s-own-routes-lose-their-request-body.md
T

47 lines
2.8 KiB
Markdown

---
id: plugin-infrastructure/a-plugin-s-own-routes-lose-their-request-body
title: "A plugin that has a sidecar proxy loses the request body on its OWN routes"
theme: plugin-infrastructure
status: done
created: 2026-08-27
date_source: stated
order: 9
---
**`createSidecarProxy` registers the plugin's whole mount prefix with `registerProxiedPrefix`, and
`bodyParser` then skips every request under it — including routes the plugin serves itself.** Those
routes get `ctx.get('body') === {}` and every field reads as missing.
Found on 2026-08-27 extracting caldav: `POST /api/caldav/provision/ios` answered
`{"error":"deviceLabel is required"}` for a request that carried one. It had never happened before
because app passwords lived at `/api/dav` — a second top-level prefix that was not proxied — and one
prefix per plugin is the rule that ended that arrangement.
**The skip itself is correct and must not be removed.** Parsing a body that is only passing through is
wasted for JSON and *corrupting* for `multipart/form-data`: Hono caches the parsed `FormData` and
re-serialises it with a NEW boundary while the proxy forwards the ORIGINAL content-type header naming
the old one, so every upload fails with "Multipart: Unexpected end of form". That is why the mechanism
exists.
**The narrow fix is that the proxy should register only the sub-path it actually serves.** Caldav's
sidecar answers `_officer/*` and nothing else, so `/api/caldav/_officer` is the honest claim; the rest of
the prefix is the plugin's own and should be parsed normally. That means `createSidecarProxy` taking the
proxied sub-path rather than assuming the whole mount.
Not done on the night it was found: it is shared machinery every sidecar depends on, and the change was
diagnosed while a phone was waiting. **The caldav plugin works around it locally** — `jsonBody()` in
`api/app-passwords.ts` reads `ctx.req.json()` directly, with the reasoning inline.
**Who else is affected:** every plugin that mixes its own routes with a sidecar proxy under one prefix,
which is most of them. They have not hit it because their own routes are GETs, or because their writes go
to the sidecar. A plugin adding its first non-proxied POST will hit it, and the symptom — a required
field reported missing on a request that plainly has it — points at the caller rather than at this.
---
**Done 2026-08-27** (`f00abe81` in the platform, and caldav's own repo). `createSidecarProxy` takes
`proxiedSubPath`, so a proxy claims only what it forwards — caldav claims `/api/caldav/_officer` and its
own routes are parsed normally. Omitted still means the whole prefix, so no other plugin changed.
Caldav's local `jsonBody()` workaround is gone. Verified: a POST to the plugin's own route read its body,
and the proxied namespace still reaches the sidecar.