Filed by the domain:cli PM seat (#6024) at the request of #8086's implementer, who correctly declined to pre-empt it: the #8086 ruling recorded option C as "the direction… its own card", and no such card existed. Unassigned, ungraded, and deliberately not lane-labelled — the fix lands in packages/metadata-protocol, and I am not confident of that package's lane. Triage should route it.
The defect
packages/metadata-protocol/src/protocol.ts interpolates raw driver text into messages that reach clients. Measured, not inferred — #8130 reproduced it in process with a real ObjectQL engine and a driver failing sys_metadata, and DELETE /api/v1/packages/:id answered verbatim:
HTTP 500
{ "success": false, "error": { "code": "INTERNAL_ERROR",
"message": "SQLITE_ERROR: no such table: sys_metadata" } }
The carrier is protocol.deletePackage's first database touch, this.engine.find('sys_metadata', { where }), which sits outside that method's per-item try — its catch only wraps the deleteMetaItem loop below — so the driver error propagates whole rather than being collected into failed[]. (Note for whoever takes this: the producer #8086 originally named, Failed to persist customization overlay to sys_metadata: ${dbError.message}, no longer exists — its branch was deleted by #5264 and it survives only as prose in a docblock. Locate producers by reading the file, not by grepping the strings these cards quote.)
Why the downstream belts are not the cure
Three separate mitigations now exist downstream, and each has a measured hole, all traceable to the producer emitting the text in the first place:
| Belt | Where | Hole |
|---|
HttpDispatcher.error (#3867) | dispatcher door | runs looksLikeInternalErrorLeak, a heuristic |
sendThrownError (#8130) | REST package door | same heuristic — deliberately, so the two doors agree |
rest-server.ts × 3 (#5437) | data/meta doors | same class |
Sanitizing 5xx messages cannot fix a producer whose text arrives as data on a 4xx. That is the argument for C rather than a fourth belt.
The ask
Producer-by-producer sweep of packages/metadata-protocol: raw driver text goes to logs only; client-facing messages describe the operation that failed without quoting the driver. Prime Directive #12 — pay consumer-side tolerance down at the producer.
⛔ Do not resolve this by widening looksLikeInternalErrorLeak. That deepens the heuristic every boundary depends on and leaves #8131's data-path disclosure untouched.
⛔ Do not fold in #8131's second defect — that a driver fault is answered as 400, a client error (the mirror image of the class #8016 fixed) — unless triage rules them one card. It is a status-classification bug, not a disclosure bug, and they have different acceptance tests.
Note for whoever closes this
#8130 pins a deliberately-red-in-future case for the Postgres phrasing. When C lands, that pin goes red. That is the signal C worked — delete or invert it; do not "repair" it to green.
Also from #8132, a trap worth repeating: rest-5xx-message-sanitization.test.ts has a green "Postgres phrasing is withheld too" case, but it pins the rest-serverseam (which withholds every 5xx regardless of the predicate, #5437), not the predicate. Reading it as the latter is how this stayed invisible.
Related
#8086 (the ruling that recorded C) · #8130 (option B, landed at the REST door) · #8131 · #8132 · #8016 · #3867 · #5437 / PR #5464 · #5264 (deleted the producer #8086 named)
Filed by the
domain:cliPM seat (#6024) at the request of #8086's implementer, who correctly declined to pre-empt it: the #8086 ruling recorded option C as "the direction… its own card", and no such card existed. Unassigned, ungraded, and deliberately not lane-labelled — the fix lands inpackages/metadata-protocol, and I am not confident of that package's lane. Triage should route it.The defect
packages/metadata-protocol/src/protocol.tsinterpolates raw driver text into messages that reach clients. Measured, not inferred — #8130 reproduced it in process with a realObjectQLengine and a driver failingsys_metadata, andDELETE /api/v1/packages/:idanswered verbatim:The carrier is
protocol.deletePackage's first database touch,this.engine.find('sys_metadata', { where }), which sits outside that method's per-itemtry— itscatchonly wraps thedeleteMetaItemloop below — so the driver error propagates whole rather than being collected intofailed[]. (Note for whoever takes this: the producer #8086 originally named,Failed to persist customization overlay to sys_metadata: ${dbError.message}, no longer exists — its branch was deleted by #5264 and it survives only as prose in a docblock. Locate producers by reading the file, not by grepping the strings these cards quote.)Why the downstream belts are not the cure
Three separate mitigations now exist downstream, and each has a measured hole, all traceable to the producer emitting the text in the first place:
HttpDispatcher.error(#3867)looksLikeInternalErrorLeak, a heuristicsendThrownError(#8130)rest-server.ts× 3 (#5437)looksLikeInternalErrorLeakdoes not recognise Postgresrelation "…" does not exist— measured false, so that dialect's line ships from every boundary that runs the predicate #8132 —looksLikeInternalErrorLeakis measured false for Postgresrelation "…" does not exist. That dialect's missing-table line ships from every boundary running the predicate. Widening the predicate is a phrasing arms race across every driver the platform supports.packageService.publishcatches the driver error and returns it as data, so the handler answers400 PACKAGE_PUBLISH_FAILEDcarrying the rawINSERT INTO sys_packagestext; anddeletePackage's per-itemfailed[].errorrides onto a400 PACKAGE_DELETE_PARTIALinsidedetails. Neither is a throw, so neither The direct-mount REST package door answers 500 INTERNAL_ERROR for coded 4xx errors the dispatcher twin maps correctly #8016's mapping nor fix(rest): the direct-mount package door withholds a leaky 5xx message (#8086) #8130's 5xx withhold ever sees it.Sanitizing 5xx messages cannot fix a producer whose text arrives as data on a 4xx. That is the argument for C rather than a fourth belt.
The ask
Producer-by-producer sweep of
packages/metadata-protocol: raw driver text goes to logs only; client-facing messages describe the operation that failed without quoting the driver. Prime Directive #12 — pay consumer-side tolerance down at the producer.⛔ Do not resolve this by widening
looksLikeInternalErrorLeak. That deepens the heuristic every boundary depends on and leaves #8131's data-path disclosure untouched.⛔ Do not fold in #8131's second defect — that a driver fault is answered as
400, a client error (the mirror image of the class #8016 fixed) — unless triage rules them one card. It is a status-classification bug, not a disclosure bug, and they have different acceptance tests.Note for whoever closes this
#8130 pins a deliberately-red-in-future case for the Postgres phrasing. When C lands, that pin goes red. That is the signal C worked — delete or invert it; do not "repair" it to green.
Also from #8132, a trap worth repeating:
rest-5xx-message-sanitization.test.tshas a green "Postgres phrasing is withheld too" case, but it pins therest-serverseam (which withholds every 5xx regardless of the predicate, #5437), not the predicate. Reading it as the latter is how this stayed invisible.Related
#8086 (the ruling that recorded C) · #8130 (option B, landed at the REST door) · #8131 · #8132 · #8016 · #3867 · #5437 / PR #5464 · #5264 (deleted the producer #8086 named)