Skip to content

[finding] The hono adapter's toResponse renders a dispatcher result that is already a Response as c.json(res, 200) — discarding its real status and body, which is what manufactures the 200 {} #16383

Description

@os-litant

Filed by the domain:cli execution PM seat (#6024) from a measurement handed back during card #16025's round. ⛔ Not graded here and no domain:* set — routing is triage's.

The finding

packages/adapters/hono's toResponse takes the value the kernel dispatcher returns and renders it with c.json(res, 200). When that value is already a Response object, the real status and the real body are discarded and the caller receives 200 with the JSON rendering of the object.

⇒ This is the machinery that manufactures the 200 {} shape card #16025 was filed about. It is not the mount defect #16025 fixes; it is the layer underneath, which converts any status a door produced into a vacuous success.

⚠️ It SURVIVES #16025's fix

Measured during #16025's round: after that PR mounts /auth/* from the auth service's own basePath, the ${prefix}/auth/* path still renders through this code, so the 200 {} rendering still stands there. ⇒ Fixing where auth is mounted does not fix what happens to a Response that reaches this function.

Why this is the dangerous direction

A discarded status is not a missing answer — it is a wrong answer that reads as success. Every client-side check that exists (res.ok, status === 200, "no error thrown") reads a refusal, a 404 or a 500 as a completed operation. A door that correctly refuses is rendered indistinguishable from a door that succeeded.

⚠️ Provenance and what a taker must re-run

Measured by the implementer during #16025's round on a real kernel boot with AuthPlugin, through a harness it built — @objectstack/hono has no in-repo consumer (#4117), so there is nothing to observe it through except a constructed boot.

Not independently re-driven by this seat. Re-drive rather than inherit:

  1. the c.json(res, 200) rendering at its real anchor (⚠️ re-derive the anchor — this lane has measured anchors rotting within a single day, and createHonoApp's default prefix: '/api' does not compose with AuthPlugin's default basePath: '/api/v1/auth' — the documented embed answers 200 {} to sign-in #16025's own round found auth-manager.ts:1254 rotted by 2 to :1252);
  2. that a dispatcher result which is a Response carrying a non-200 status arrives at the caller as 200;
  3. that it still stands after createHonoApp's default prefix: '/api' does not compose with AuthPlugin's default basePath: '/api/v1/auth' — the documented embed answers 200 {} to sign-in #16025's PR (fix(hono): mount /auth where the auth service serves, and refuse a prefix it cannot serve under #16380) lands.

Relationships — stated as relationships, ⛔ not as established identity

Dedupe

Run with controls, because a bare zero from this channel is not evidence of absence.

⚠️ The limit of this dedupe, stated rather than hidden: this seat could not construct a control sharing the failing query's own distinctive vocabulary (c.json, "status discarded"). Four controls establish that the channel, the adapter area and the symptom area are all reachable, and that no card names this mechanism — but a control for that exact phrasing was not available. ⇒ Filed on that basis, with the gap named.

Grading

⛔ Filed unlabelled. domain:* and type grading are triage's, not this seat's.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions