Filed unassigned by the #15780 dev while landing the per-dialect $icontains fold. Recording only — no severity asserted, routing is triage's. ⛔ Not fixed as a rider on that PR: different package (packages/types), different defect class (a message-leak heuristic, not SQL dialect compilation).
Dedup: one targeted MCP search_issues (2026-09-05; the repo-scoped REST channel answers 403 on this seat) for looksLikeInternalErrorLeak returned 0 open cards. That zero is a live reading, not a dead channel — a second search in the same session with a control term returned #15780 and #15683, so the channel was answering.
Measured
looksLikeInternalErrorLeak (packages/types/src/error-leak.ts:207) decides whether a >=500 message is withheld from the caller and replaced with INTERNAL_ERROR_MESSAGE. Run against the real message a SQLite engine raises for an unknown function, with controls in the same run:
looksLikeInternalErrorLeak('no such function: translate') = false ← the leak
looksLikeInternalErrorLeak('no such column: bogus_dim') = true ← control, the #5520 case
looksLikeInternalErrorLeak('no strategy can handle query') = false ← control, a self-authored fault stays readable by design
declaresServerFault(new Error('no such function: translate')) = false
declaresServerFault({ status: 503, code: 'X' }) = true ← control
Both controls fire in the directions their own docblocks state, so the false on the first line is a reading about that message rather than about a broken probe.
Why it matters
declaresServerFault false means a raw driver error is an UNDECLARED fault, so POST /analytics/dataset/query falls to its generic terminal (rest-server.ts, arm ③b):
res.status(500).json({ code: 'ANALYTICS_QUERY_FAILED', error: outward, ...markExtra });
outward = declaresServerFault(error) || looksLikeInternalErrorLeak(msg)
? INTERNAL_ERROR_MESSAGE
: clientMsg.slice(0, 500);
With both predicates false, outward is the engine's own text. The sibling face /analytics/query exits through dispatcher-plugin.errorResponseBase, which applies the same predicate, so both doors read the same way.
The asymmetry is the finding: no such column: is recognised as driver phrasing and no such function: is not, though they are the same SQLite error family and arrive by the same route. #5520 closed the no such column case after a caller received physical table and column names; a no such function message carries less (the function name we emitted), which may be why nobody noticed, but the classification is inconsistent rather than deliberately tiered — nothing in error-leak.ts names the distinction.
NOT asserted
Whether the right answer is to widen the heuristic (⚠️ #5367 removed message sniffing on the analytics door on purpose, so widening it is a real trade-off, not obviously correct), to leave it, or to make driver errors DECLARE themselves so the structural declaresServerFault rule catches them without any prose test. That last option is the shape #5811 promoted and may make the heuristic's coverage moot for this family.
Reachability after #15780
Still live. #15780 stops translate() reaching SQLite on the sqlite and mysql arms, but the unknown residue — a SQLite datasource whose host wires no sqlDialect hook — still compiles translate() and still produces exactly this message. It is also not specific to translate(): any function a dialect lacks lands here.
Related
#15780 (where this was measured) · #5520 (the no such column leak this predicate closed) · #5367 (retired the analytics message-sniffing list) · #5811 (declaresServerFault promoted to @objectstack/types) · ADR-0112 (the envelope)
Filed unassigned by the #15780 dev while landing the per-dialect
$icontainsfold. Recording only — no severity asserted, routing is triage's. ⛔ Not fixed as a rider on that PR: different package (packages/types), different defect class (a message-leak heuristic, not SQL dialect compilation).Dedup: one targeted MCP
search_issues(2026-09-05; the repo-scoped REST channel answers 403 on this seat) forlooksLikeInternalErrorLeakreturned 0 open cards. That zero is a live reading, not a dead channel — a second search in the same session with a control term returned #15780 and #15683, so the channel was answering.Measured
looksLikeInternalErrorLeak(packages/types/src/error-leak.ts:207) decides whether a >=500 message is withheld from the caller and replaced withINTERNAL_ERROR_MESSAGE. Run against the real message a SQLite engine raises for an unknown function, with controls in the same run:Both controls fire in the directions their own docblocks state, so the
falseon the first line is a reading about that message rather than about a broken probe.Why it matters
declaresServerFaultfalse means a raw driver error is an UNDECLARED fault, soPOST /analytics/dataset/queryfalls to its generic terminal (rest-server.ts, arm ③b):With both predicates false,
outwardis the engine's own text. The sibling face/analytics/queryexits throughdispatcher-plugin.errorResponseBase, which applies the same predicate, so both doors read the same way.The asymmetry is the finding:
no such column:is recognised as driver phrasing andno such function:is not, though they are the same SQLite error family and arrive by the same route. #5520 closed theno such columncase after a caller received physical table and column names; ano such functionmessage carries less (the function name we emitted), which may be why nobody noticed, but the classification is inconsistent rather than deliberately tiered — nothing inerror-leak.tsnames the distinction.NOT asserted
Whether the right answer is to widen the heuristic (⚠️ #5367 removed message sniffing on the analytics door on purpose, so widening it is a real trade-off, not obviously correct), to leave it, or to make driver errors DECLARE themselves so the structural
declaresServerFaultrule catches them without any prose test. That last option is the shape #5811 promoted and may make the heuristic's coverage moot for this family.Reachability after #15780
Still live. #15780 stops
translate()reaching SQLite on thesqliteandmysqlarms, but theunknownresidue — a SQLite datasource whose host wires nosqlDialecthook — still compilestranslate()and still produces exactly this message. It is also not specific totranslate(): any function a dialect lacks lands here.Related
#15780 (where this was measured) · #5520 (the
no such columnleak this predicate closed) · #5367 (retired the analytics message-sniffing list) · #5811 (declaresServerFaultpromoted to@objectstack/types) · ADR-0112 (the envelope)