A route mounted through IHttpServer.getRawApp() funnels through neither the Hono adapter's wrap() nor any registrar wrapper, so an escaped throw there is answered by Hono's own default handler: 500 text/plain "Internal Server Error". No ADR-0112 envelope, no success flag, no code — and the thrown value's own declared status / code are discarded.
Split out of #16545 by the executing seat, at the domain:cli PM seat's explicit direction (comment 5570159125, restated in 5575064522):
raw Hono mount 在本卡范围之外,且必须在 PR 里写明,而不是默默略过。
and
⛔ Not filed by this note: the raw-mount defect itself (500 text/plain, no ADR-0112 envelope). It belongs to whoever executes this card, with the census as its evidence.
Measured, on origin/main + the #16545 fix
Both throws below were driven through one HonoHttpServer, the raw pair mounted exactly the way marketplace-install-local-plugin.ts mounts (getRawApp().get(...)), the wrapped pair through the ordinary IHttpServer verb:
/raw/envelope -> 500 text/plain; charset=UTF-8
Internal Server Error
/raw/plain -> 500 text/plain; charset=UTF-8
Internal Server Error
/wrapped/envelope -> 503 application/json
{"success":false,"error":{"code":"SERVICE_UNAVAILABLE","message":"The authorization store could not be read."}}
/wrapped/plain -> 500 application/json
{"success":false,"error":{"code":"INTERNAL_ERROR","message":"No response from handler"}}
/raw/envelope and /raw/plain are byte-identical. That is the measurement that made the seat rule the door out of #16545's scope: #16545's required pin — "a non-envelope throw still answers 500 with no cause in the body" — is green on this door before any fix exists, so writing it there would have measured nothing.
Why this is a contract violation and not a style note
BaseResponseSchema / ApiErrorSchema (ADR-0112) is the declared refusal envelope for this platform's HTTP surface. These doors are mounted on the same app, under the same prefix, and answer a shape no client parser can read — a caller doing body.error.code gets undefined, and a caller doing res.json() throws on Internal Server Error.
The door population (measured, getRawApp() consumers)
Not one door — the escape hatch has several consumers:
packages/cloud-connection/src/marketplace-install-local-plugin.ts (the census's exemplar)
packages/cloud-connection/src/marketplace-proxy-plugin.ts
packages/cloud-connection/src/cloud-connection-plugin.ts
packages/cloud-connection/src/runtime-config-plugin.ts
packages/metadata/src/plugin.ts (HMR routes)
packages/plugins/plugin-auth/src/auth-plugin.ts (wildcard routing)
packages/plugins/plugin-approvals/src/approvals-plugin.ts
packages/cli/src/utils/console.ts (two sites)
What is NOT being proposed here
⛔ Not "bring the raw mounts under wrap()" by default. The seat's own reasons against that stand and are worth carrying into whatever decision this card gets:
- Bringing a raw mount under the wrapper is a transport-level change with its own blast radius, and it is not in this card's named surface.
- ⭐ The repo's working shape is route-level, not transport-level.
So this needs a decision on instrument before it needs an implementation: a Hono onError hook on the raw app, a shared catch helper each raw door opts into, or bringing the doors back onto IHttpServer. Each has a different blast radius and a different answer for the doors that deliberately want framework-native mounting.
Related
Generated by Claude Code
A route mounted through
IHttpServer.getRawApp()funnels through neither the Hono adapter'swrap()nor any registrar wrapper, so an escaped throw there is answered by Hono's own default handler:500 text/plain "Internal Server Error". No ADR-0112 envelope, nosuccessflag, nocode— and the thrown value's own declaredstatus/codeare discarded.Split out of #16545 by the executing seat, at the
domain:cliPM seat's explicit direction (comment 5570159125, restated in 5575064522):and
Measured, on
origin/main+ the #16545 fixBoth throws below were driven through one
HonoHttpServer, the raw pair mounted exactly the waymarketplace-install-local-plugin.tsmounts (getRawApp().get(...)), the wrapped pair through the ordinaryIHttpSerververb:/raw/envelopeand/raw/plainare byte-identical. That is the measurement that made the seat rule the door out of #16545's scope: #16545's required pin — "a non-envelope throw still answers 500 with no cause in the body" — is green on this door before any fix exists, so writing it there would have measured nothing.Why this is a contract violation and not a style note
BaseResponseSchema/ApiErrorSchema(ADR-0112) is the declared refusal envelope for this platform's HTTP surface. These doors are mounted on the same app, under the same prefix, and answer a shape no client parser can read — a caller doingbody.error.codegetsundefined, and a caller doingres.json()throws onInternal Server Error.The door population (measured,
getRawApp()consumers)Not one door — the escape hatch has several consumers:
packages/cloud-connection/src/marketplace-install-local-plugin.ts(the census's exemplar)packages/cloud-connection/src/marketplace-proxy-plugin.tspackages/cloud-connection/src/cloud-connection-plugin.tspackages/cloud-connection/src/runtime-config-plugin.tspackages/metadata/src/plugin.ts(HMR routes)packages/plugins/plugin-auth/src/auth-plugin.ts(wildcard routing)packages/plugins/plugin-approvals/src/approvals-plugin.tspackages/cli/src/utils/console.ts(two sites)What is NOT being proposed here
⛔ Not "bring the raw mounts under
wrap()" by default. The seat's own reasons against that stand and are worth carrying into whatever decision this card gets:So this needs a decision on instrument before it needs an implementation: a Hono
onErrorhook on the raw app, a sharedcatchhelper each raw door opts into, or bringing the doors back ontoIHttpServer. Each has a different blast radius and a different answer for the doors that deliberately want framework-native mounting.Related
status+ registeredcodeas that envelope, not as bare500 INTERNAL_ERROR "No response from handler"— #15999's shared half #16545 — the direct-mount half, which repairswrap()and names this door in its PR as a known-unreached oneAuthzStoreUnavailableErrorthe admin guard re-raises reaches the wire as500 INTERNAL_ERROR, not the503 SERVICE_UNAVAILABLEthe brand declares #15999 — the ruling both halves come fromos i18n extract --check --dry-runexits 0 having compared nothing — a--checkthat passes without checking, i.e. a false green in CI #16480 — the false-green class the vacuous-pin reasoning belongs toGenerated by Claude Code