Observation
Found while implementing objectui#7959 (extracting the ADR-0112 failure-envelope read that
fetchPackages, apiJson and duplicatePackage each held their own copy of). That card's
declared file surface is views/studio-design/packages-io.ts and PackagesPage.tsx's
apiJson, so this one was deliberately left alone rather than fixed as a rider.
packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx holds a fourth
instance of the same ladder, character for character the one apiJson had before objectui#7959:
// PackageFormDialog.tsx:62
payload?.error?.message || payload?.error || payload?.message || `Request failed (${res.status})`;
It reads the diagnostic error.message and stops. So it drops both of the things the other
three readers now render:
error.userMessage — the producer's #9934 marked channel. Presence of the field IS the
producer's marking, and the envelope writer's rule is that "a consumer that sees the field
renders it verbatim". In the 5xx band the door substitutes the generic Internal server error into message while the mark rides through untouched, so on a marked 500/503 this
dialog shows the author the generic sentence and discards the specific one written for them.
error.code, which never reaches the author at all.
Why it is live, not theoretical
This dialog POSTs and PUTs /api/v1/packages — the same route family objectui#7959 measured.
Both doors that serve it spread the marked channel onto the wire: sendThrownError
(@objectstack/rest package-routes.ts) and its dispatcher twin errorFromThrown
(@objectstack/runtime http-dispatcher.ts), which the REST door's own note calls "byte for
byte the dispatcher twin's expression … which serves this same path and has emitted the
channel since #9934". The framework pins the pair wire-side in package-door-user-message.test.ts.
Create/edit are exactly where an author meets a refusal that names what to fix — a namespace
rule, a capability to grant, a version already published.
The fix is now mechanical
objectui#7959 extracted the rule to packages/app-shell/src/utils/apiErrorEnvelope.ts
(readEnvelopeFailureText), pinned in apiErrorEnvelope.test.ts. This site becomes:
const msg =
readEnvelopeFailureText(payload) || payload?.error || payload?.message || `Request failed (${res.status})`;
— the two rungs below stay, because a bare-string error and a top-level message are older
runtimes' shapes and not this envelope. Wants its own pin over the four combinations
(message only / userMessage only / both / neither) plus the code interaction, matching
PackagesPage.envelopeUserMessage.test.tsx.
Re-check
sed -n '55,70p' packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx
grep -rn "readEnvelopeFailureText" packages/app-shell/src/
Related, not duplicates: objectui#7959 (the three readers this one was left out of),
objectui#7938 / PR objectui#7960 (fetchFullPackage), objectui#7368 (the report channel),
objectui#4634 (the same family: a reader discarding the kernel's sentence).
Observation
Found while implementing objectui#7959 (extracting the ADR-0112 failure-envelope read that
fetchPackages,apiJsonandduplicatePackageeach held their own copy of). That card'sdeclared file surface is
views/studio-design/packages-io.tsandPackagesPage.tsx'sapiJson, so this one was deliberately left alone rather than fixed as a rider.packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsxholds a fourthinstance of the same ladder, character for character the one
apiJsonhad before objectui#7959:It reads the diagnostic
error.messageand stops. So it drops both of the things the otherthree readers now render:
error.userMessage— the producer's #9934 marked channel. Presence of the field IS theproducer's marking, and the envelope writer's rule is that "a consumer that sees the field
renders it verbatim". In the 5xx band the door substitutes the generic
Internal server errorintomessagewhile the mark rides through untouched, so on a marked 500/503 thisdialog shows the author the generic sentence and discards the specific one written for them.
error.code, which never reaches the author at all.Why it is live, not theoretical
This dialog POSTs and PUTs
/api/v1/packages— the same route family objectui#7959 measured.Both doors that serve it spread the marked channel onto the wire:
sendThrownError(
@objectstack/restpackage-routes.ts) and its dispatcher twinerrorFromThrown(
@objectstack/runtimehttp-dispatcher.ts), which the REST door's own note calls "byte forbyte the dispatcher twin's expression … which serves this same path and has emitted the
channel since #9934". The framework pins the pair wire-side in
package-door-user-message.test.ts.Create/edit are exactly where an author meets a refusal that names what to fix — a namespace
rule, a capability to grant, a version already published.
The fix is now mechanical
objectui#7959 extracted the rule to
packages/app-shell/src/utils/apiErrorEnvelope.ts(
readEnvelopeFailureText), pinned inapiErrorEnvelope.test.ts. This site becomes:— the two rungs below stay, because a bare-string
errorand a top-levelmessageare olderruntimes' shapes and not this envelope. Wants its own pin over the four combinations
(
messageonly /userMessageonly / both / neither) plus the code interaction, matchingPackagesPage.envelopeUserMessage.test.tsx.Re-check
Related, not duplicates: objectui#7959 (the three readers this one was left out of),
objectui#7938 / PR objectui#7960 (
fetchFullPackage), objectui#7368 (the report channel),objectui#4634 (the same family: a reader discarding the kernel's sentence).