Uh oh!
There was an error while loading. Please reload this page.
fix(rest): anchor looksLikeMissingRelation on the driver's quoted template - #8644
Conversation
…plate (#8264) `mapDataError`'s Postgres limb read `relation` and `does not exist` anywhere in the message, not necessarily the same sentence, so ordinary business prose using both words matched. Anchored on the quoted identifier Postgres always emits, mirroring #8132's anchor for the shared `looksLikeInternalErrorLeak` predicate in @objectstack/types (deliberately NOT reused here — it answers a different question and its other limbs are unrelated to this file's 404-vs-500 attribution). Both call sites of the predicate are covered: the DATA_STORE_FAULT (500) gate the issue named, and the looksLikeUnknownObject (404) limb the issue's own text did not measure. Tests pin both decision paths. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7vaLs7bhBPi9m3JyzkhDj
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also reference the affected code. These are read-only:
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8264
What changed
mapDataError's Postgres limb (packages/rest/src/rest-server.ts) readrelationanddoes not existanywhere in the message, not necessarily thesame sentence:
does not existis ordinary business English ("This relation does not existin the diagram" — the exact string
error-leak.test.tsalready pins as anegative case for #8132's shared leak predicate). Anchored the Postgres limb
on the driver's own quoted template instead, the same technique #8132 used
for
looksLikeInternalErrorLeakin@objectstack/types:no such tableandtable not foundare untouched — both are exactadjacent phrases, not a two-word conjunction, so they were never exposed to
this hole.
Design decision: one predicate, not "two widths, on purpose"
The card asked me to decide whether this should share the
@objectstack/typesanchored helper, or whether
relation-sub-object.ts's "two widths, onpurpose" precedent applies here. Read all three sources named in the card:
looksLikeInternalErrorLeak. That predicate answers adifferent question — "may this message be withheld from the client at
all?" — and its other limbs (
sqlite_,unique constraint,foreign key,a bare SQL statement) have nothing to do with this file's question ("is
this specifically an unknown-relation condition, for the 404-vs-500
split?"). Its own module doc already warns that
relation-sub-object.ts'sfunctions are "related but NOT reusable" for the same reason; the same
argument applies to this predicate.
relation-sub-object.ts's "two widths, on purpose" does NOT extend tothe two USES inside this file. That precedent is about two different
questions (extract which column? vs. exclude "is this a sub-object?") that
happen to share a substring, where a miss costs opposite things at each
call site. Here, both of
looksLikeMissingRelation's two readers —the
DATA_STORE_FAULT()500 gate and thelooksLikeUnknownObject404 limb— are asking the exact same question ("does this message look like a
missing-relation condition?"). There's no reason for them to disagree, and
missingRelationIsObjectalready provides the attribution safety net thatwould otherwise justify a width difference (see measurement below). So:
one predicate, module-scoped, shared by both readers.
#5462comment, directly above this code, argues the safedirection on ambiguity is LOUD (a logged 500), never a silent 404. Tightening
moves the predicate's remaining false-positive risk toward the terminal
UNCLASSIFIED_FAULT()(500, logged) rather than a misleading 404 — consistentwith that stated philosophy, not in tension with it.
Both decision paths measured — not just the reported one
The card's own text discusses only the
DATA_STORE_FAULT()500 path. Thepredicate actually feeds two decisions, and I measured both:
Decision 1 — the 500 gate (
if (looksLikeMissingRelation && !missingRelationIsObject(...)) return DATA_STORE_FAULT()):Decision 2 — the
looksLikeUnknownObject404 limb, which the card's owncounter-example cannot actually reach:
missingRelationIsObject's ownextraction regex fails on
'This relation does not exist in the diagram'(there is no valid identifier between
relationand the literaldoes not existtail), so that string was already routed to decision 1 on everyinput, old or new. To exercise decision 2 I built a second case where
missingRelationIsObjectdoes extract a name — unquoted, since its ownPostgres branch tolerates unquoted identifiers (a narrower, different
question: "which relation is named", not "is this the driver's real
template"):
Both are pinned in
rest-unknown-object-heuristic.test.ts(§5a/§5b), andthe genuine quoted-form cases (
§5c/§5d) confirm neither decision pathregressed for real driver output — all three quote styles Postgres could use
still trip the 500 gate, and a quoted, correctly-attributed relation name
still reaches the 404 limb exactly as before.
missingRelationIsObject's own extraction regex is left as-is: it's onlyever invoked once
looksLikeMissingRelationhas already gated true, so forevery input that reaches it post-fix, the message already carries the
quoted phrase — its optional-quote leniency is exercised on inputs that no
longer reach it. No changed behavior there, and no separate fix needed.
Reachable production path: none found
Per the card's own honesty and the branch's own
#5462comment — theprimary producer of the missing-relation 404 is
#3770's registry gate,which throws
code: 'OBJECT_NOT_FOUND'and is matched far above thisheuristic — I looked for a live path that would actually reach this loose
conjunction with an ambiguous message and found none. This is
consistency/invariant restoration between two spellings of one question
(this file's copy vs. the
@objectstack/typesanchor #8132 taught), not afix for a demonstrated live misclassification. Not inflating the severity.
Reverse verification
Prediction recorded before running: reverting only
rest-server.ts'spredicate to the pre-fix conjunction (test file left at HEAD) turns the two
new decision-path tests (
§5ax2,§5b) RED and leaves everything else(including
§5c/§5d, the real quoted-driver-phrasing cases, and the other23 pre-existing tests in the file) GREEN — ordinary RED, not an inverted
direction.
Measured:
Restored (
git checkout THIS_BRANCH -- packages/rest/src/rest-server.ts),rebuilt, reran: 26/26 passing again. Matches the prediction exactly.
Tests
Gates (derived from the changed paths via
node scripts/pm/dispatch-gates.mjs):Scope
Diff is confined to
packages/rest/src/rest-server.ts(the one hot region:the module-scoped
RELATION_DOES_NOT_EXISTconstant + thelooksLikeMissingRelationdefinition, no wide reformatting) and its own testfile.
.changeset/anchor-missing-relation-quoted-template.mdadded (patch,@objectstack/rest). Nocontent/docs/releases/edits.Generated by Claude Code