Skip to content

[finding] the #13904 engine-slot repair is re-collapsed at objectQLProvider's SECOND consumer — GET /meta/object/:name/state/:field answers 404 for a wired-and-failing engine #15405

Description

@os-litant

Found while running the #14251 phase-1 consumer census (read-only). Recorded for triage; no severity asserted.

Measured by reading origin/main at a56baa2bdf. Located by symbol, not by line — the line numbers below are that commit's.

The shape

objectQLProvider has two consumers in packages/rest/src/rest-server.ts, and #13904 (PR #14250) repaired the slot for one of them:

consumer line how it reaches the provider
computeExecCtx, the authorization-input seam 2361 wiredEngineOrLoud(Boolean(this.objectQLProvider), ...)keeps the two facts apart (#13476)
GET {basePath}/meta/object/:name/state/:field 7453 await this.objectQLProvider(environmentId).catch(() => undefined)re-collapses them

The second one converts every rejection back into the same undefined a never-registered engine produces, three lines before the answer is chosen:

const ql = this.objectQLProvider
    ? await this.objectQLProvider(environmentId).catch(() => undefined)
    : undefined;
const schema = (ql as any)?.registry?.getObject?.(name);
if (!schema) {
    res.status(404).json({ error: { code: 'NOT_FOUND', message: 'Object not found' } });

⇒ on this route, an engine that was wired and failed to build and an engine that was never registered both answer 404 NOT_FOUND · "Object not found" — the same disguise #13476 measured at the package door (there it wore a 403), and the same one its sibling seam now answers 503 for.

Why this line is NEWLY load-bearing, and was not before

Before #13904 the shipped provider was try { ... } catch { return undefined; }, so it could not reject: the .catch at 7453 was dead code and the collapse happened one layer earlier. #13904 made the provider re-raise every unbranded rejection precisely so a consumer could see it. This consumer catches it back. So the repair reaches one of the slot's two consumers, and the other is unchanged by it.

⛔ Not a defect in #13904 — its card and its pins are scoped to the computeExecCtx seam, and they hold. This is the second consumer nobody enumerated.

A second, narrower observation at the same line

provider(environmentId).catch(...) attaches to the promise the call RETURNS, so it can only ever see a rejection. A host that wires a non-async provider — which the seam's declared type cannot prevent, and RestServer's constructor is the public wiring point — throws while the expression is still being evaluated, and the .catch is never reached. That is exactly the asymmetry seamOrUndefined was introduced to end (its docblock measures it at settingsServiceProvider: 200 when the provider was async, 401 when it was not).

Two provider call sites in rest-server.ts still carry the retired spelling:

  • rest-server.ts:7453objectQLProvider (above)
  • rest-server.ts:9286emailServiceProvider, in POST {basePath}/email/send

Every other provider call site in the file either goes through seamOrUndefined / wiredEngineOrLoud or wraps the call in a real try/catch, which sees both fault shapes.

⚠️ Neither is reachable from the shipped wiring today: both providers in rest-api-plugin.ts are declared async. This is residue of the #13280 class at an embedder-reachable seam, not a live wire defect.

Scope

Not folded into anything. #14251 phase 1 is read-only, and its file surface is the provider closures in rest-api-plugin.ts; both observations here are consumer-side, in rest-server.ts. The repair for the first is a one-line change of spelling at 7453 plus the ruling that goes with it — is "the engine is wired and broken" a 503 on a metadata read, as it already is on the package door — which is a per-consumer judgement and the reason this is a card and not a rider.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions