Found while measuring #13255. ⛔ No severity is asserted here; this records what was driven and what came back, for triage.
The shape
RestServer.computeExecCtx reaches its host-wired providers as, e.g.
const settings = this.settingsServiceProvider
? await this.settingsServiceProvider(environmentId).catch(() => undefined)
: undefined;
.catch is attached to the promise the call RETURNS. A provider that returns a rejecting promise is therefore absorbed at the seam and the resolution continues. A provider that throws SYNCHRONOUSLY throws before there is a promise to attach to, so the .catch is never reached and the throw lands in computeExecCtx's outer catch { return undefined; } — which discards the entire execution context, identity included.
The same asymmetry is structural at authServiceProvider, objectQLProvider and settingsServiceProvider; kernelManager.getOrCreate and authService.getApi() carry no seam .catch at all and lose the context in both directions.
What was measured
Real RestServer, real registerPackageRoutes, wired as rest-api-plugin.ts wires it. Both rows are the SAME seam and the SAME fault, differing only in how the provider fails. Harness: packages/rest/src/package-door-execctx-fault-reachability.test.ts, section 7.
settingsServiceProvider | GET /api/v1/packages |
|---|
async () = throws (a rejecting promise) | 200 — the caller keeps manage_metadata + studio.access |
() = throws (synchronous) | 401 UNAUTHENTICATED |
Both callers hold a valid session and identical grants. The wire answer is decided by whether the host happened to declare its provider async.
The 401 half is also the sharpest instance of the degrade #13255 is measuring: the fault is in a LOCALIZATION/settings input that has nothing to do with authorization, it happens AFTER identity has already resolved, and the caller is nonetheless told "Authentication is required to access this endpoint."
Not ruled here
Two candidate directions, neither taken: normalize the seams (Promise.resolve().then(() = provider(...) ) so a sync throw and a rejection agree), or narrow computeExecCtx's outer catch so a post-identity fault does not discard identity. The second is a behaviour change on a public door and is the same class of decision #13255 defers.
Refs
Found while measuring #13255. ⛔ No severity is asserted here; this records what was driven and what came back, for triage.
The shape
RestServer.computeExecCtxreaches its host-wired providers as, e.g..catchis attached to the promise the call RETURNS. A provider that returns a rejecting promise is therefore absorbed at the seam and the resolution continues. A provider that throws SYNCHRONOUSLY throws before there is a promise to attach to, so the.catchis never reached and the throw lands incomputeExecCtx's outercatch { return undefined; }— which discards the entire execution context, identity included.The same asymmetry is structural at
authServiceProvider,objectQLProviderandsettingsServiceProvider;kernelManager.getOrCreateandauthService.getApi()carry no seam.catchat all and lose the context in both directions.What was measured
Real
RestServer, realregisterPackageRoutes, wired asrest-api-plugin.tswires it. Both rows are the SAME seam and the SAME fault, differing only in how the provider fails. Harness:packages/rest/src/package-door-execctx-fault-reachability.test.ts, section 7.settingsServiceProviderasync () =throws (a rejecting promise)manage_metadata+studio.access() =throws (synchronous)Both callers hold a valid session and identical grants. The wire answer is decided by whether the host happened to declare its provider
async.The 401 half is also the sharpest instance of the degrade #13255 is measuring: the fault is in a LOCALIZATION/settings input that has nothing to do with authorization, it happens AFTER identity has already resolved, and the caller is nonetheless told "Authentication is required to access this endpoint."
Not ruled here
Two candidate directions, neither taken: normalize the seams (
Promise.resolve().then(() =provider(...))so a sync throw and a rejection agree), or narrowcomputeExecCtx's outercatchso a post-identity fault does not discard identity. The second is a behaviour change on a public door and is the same class of decision #13255 defers.Refs
.catch(() => undefined)把执行上下文解析失败静默降级为「无上下文」— 该行为在包管理门上可达什么错误状态,未测 #13255 — the card this fell out of