Filed unassigned while implementing the #11683 + #11684 fold. Out of scope there: both cards are about the 4xx band, and moving the 5xx arm is a separate contract call on a shipped route.
Measured
Found by the first draft of rest-hook-refusal-message-parity.test.ts §8e, which included this case in its door-to-door parity loop and reddened with the #11684 fix in place. Both handlers driven in process, one error object, on claude/issue-11683-route-refusal-classification:
| face | answer |
|---|
POST /api/v1/data/:object | 503 — {"error":"Internal server error","code":"SERVICE_UNAVAILABLE"} |
POST /api/v1/analytics/dataset/query | 500 — {"code":"ANALYTICS_QUERY_FAILED","error":"Internal server error"} |
The error is a producer-declared server fault: { code: 'SERVICE_UNAVAILABLE', status: 503 }.
Now pinned as measured-and-not-repaired in that file's §8f, so the bound on §8e's parity claim is visible rather than implied.
Why
resolveErrorResponse's passthrough (#5582) keeps a declared 5xx's status and code and drops only the prose — the ruling's own argument being that 502/503 are isExpectedDataStatus lifecycle outcomes that proxies and retry policies read differently from a 500, so collapsing them onto 500 destroys the declaration to protect the message.
The analytics catch's arm ① is 4xx-only by design, so a declared 5xx falls to ③'s hand-built 500 ANALYTICS_QUERY_FAILED. Both halves of that are deliberate and documented (#5352, re-argued by #5367 and #5811, and load-bearing for read-scope-sql's ten READ_SCOPE_COMPILE_FAILED refusals) — but the ruling was about withholding the message, and the status collapse rides along with it. The prose half already agrees: both faces answer INTERNAL_ERROR_MESSAGE.
Not established here
Region: packages/rest/src/rest-server.ts, the /analytics/dataset/query catch's arm ③ only.
Filed unassigned while implementing the #11683 + #11684 fold. Out of scope there: both cards are about the 4xx band, and moving the 5xx arm is a separate contract call on a shipped route.
Measured
Found by the first draft of
rest-hook-refusal-message-parity.test.ts§8e, which included this case in its door-to-door parity loop and reddened with the #11684 fix in place. Both handlers driven in process, one error object, onclaude/issue-11683-route-refusal-classification:POST /api/v1/data/:object503—{"error":"Internal server error","code":"SERVICE_UNAVAILABLE"}POST /api/v1/analytics/dataset/query500—{"code":"ANALYTICS_QUERY_FAILED","error":"Internal server error"}The error is a producer-declared server fault:
{ code: 'SERVICE_UNAVAILABLE', status: 503 }.Now pinned as measured-and-not-repaired in that file's §8f, so the bound on §8e's parity claim is visible rather than implied.
Why
resolveErrorResponse's passthrough (#5582) keeps a declared 5xx's status and code and drops only the prose — the ruling's own argument being that502/503areisExpectedDataStatuslifecycle outcomes that proxies and retry policies read differently from a500, so collapsing them onto500destroys the declaration to protect the message.The analytics catch's arm ① is 4xx-only by design, so a declared 5xx falls to ③'s hand-built
500 ANALYTICS_QUERY_FAILED. Both halves of that are deliberate and documented (#5352, re-argued by #5367 and #5811, and load-bearing forread-scope-sql's tenREAD_SCOPE_COMPILE_FAILEDrefusals) — but the ruling was about withholding the message, and the status collapse rides along with it. The prose half already agrees: both faces answerINTERNAL_ERROR_MESSAGE.Not established here
503 ANALYTICS_QUERY_FAILED,503 SERVICE_UNAVAILABLE, and "leave it at 500" are three different answers and analytics 的 filter 拒收到不了调用方:service 侧多数拒收没有 ADR-0112 信封,REST 面又用 message 正则嗅探,一律答 500 #5352's text does not distinguish them — it argues the message, not the status./analytics/queryface (throughdispatcher-plugin.errorResponseBase) has the same collapse. Not exercised.Region:
packages/rest/src/rest-server.ts, the/analytics/dataset/querycatch's arm ③ only.