Skip to content

POST /api/v1/packages/publish answers a misleading 405 (Allowed: DELETE, GET, HEAD, PATCH) — the request is absorbed by /packages/:id because the REST package registrar is not mounted on showcase #7563

Description

@huangyiirene

Symptom

POST /api/v1/packages/publish on a live showcase boot answers 405 with Allowed: DELETE, GET, HEAD, PATCH.

That method list belongs to a different route. POST is the only verb the publish surface ever had, so a 405 naming four other verbs tells the caller "this path exists, just use another method" — when in truth no publish surface is served on this host at all, and every verb in that list would be operating on a package whose id is the literal string publish.

Expected: either the real publish handler (the route is ledgered — POST /api/v1/packages/publish in packages/rest/src/rest-route-ledger.ts, source: direct-mount, disposition: server-only), or an honest 404 for a surface that is not mounted here. A 405 whose Allowed set describes another route's methods is the one answer that misinforms.

Root cause

As far as the run locates it: the REST package registrar is not mounted on showcase. POST /packages/publish is owned by packages/rest/src/package-routes.ts (which is where marketplace publish landed — the file says so explicitly: "Marketplace publish lives at /packages/publish, NOT at the bare …"), and those REST package routes are service-gated, so on a stack without the package service they never register.

With the registrar absent, the path is still matched — by /packages/:id with id = "publish" — and the router derives its Allowed header from that route's method set, which is exactly DELETE, GET, HEAD, PATCH. The path is therefore "reachable" only by accident of the id-parameter route, and the 405 is generated by the wrong owner.

Related, closed: #3610 (POST /api/v1/packages publish-vs-install shape collision, closed by PR #3621) is the reconciliation that moved publish onto /packages/publish in the first place. This is not a regression of that fix's stated acceptance — it is the residue it did not cover: what the /packages prefix answers on a host where only one of the two owners is mounted.

Still live on origin/main as of 2026-08-11 (route present in package-routes.ts and in the REST ledger; gating unchanged).

Reproduction

  1. Boot showcase on a fresh isolated file DB (SqlDriver / better-sqlite3); authenticate as admin@objectos.ai.
  2. POST /api/v1/packages/publish with any body.
  3. Observe 405 with Allowed: DELETE, GET, HEAD, PATCH — not a handler answer, and not the 404 an unmounted surface should give.
  4. Cross-check the ledger: POST /api/v1/packages/publish is a declared route, so the 405 is not "this route does not exist" either.

Source

Extracted from the QA run #7463 (framework a86db17).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions