Found while implementing #13324 (PR #13437); out of that card's scope, filed unassigned for triage. It is a residual of that repair, not a pre-existing defect.
What #13324 changed
isMissingTableError(error, readObject) now refuses the benign "table not provisioned yet" verdict when the dialect phrase names a relation other than the one the caller read. Call sites pass the object API name they were reading.
The gap
For a federated (external) object, the object API name is not the name the driver puts in the SQL. SqlDriver.registerExternalObject records external.remoteName in physicalTableByObject, and getBuilder targets that:
this.physicalTableByObject[object] ?? StorageNameMapping.resolveTableName({ name: object })
So when a federated object declares external: { remoteName: 'legacy_orders' } and that remote table is genuinely absent, the driver raises a phrase naming legacy_orders while the call site declares the object name (say crm_order). The names do not match, so the repaired predicate answers not benign for what is in fact a genuine missing table.
The comparison folds away schema/database qualifiers, the legacy namespace__short prefix and case, but it cannot fold away an arbitrary remoteName — nothing at the call site knows the mapping, which lives on the driver instance.
Reach
Only the call sites that read an arbitrary, caller-named object can hit this — a federated object is never one of the fixed sys_* reads:
ObjectQL.seedAutonumber (packages/objectql/src/engine.ts) — passes object- the cascade-delete dependents probe (same file) — passes
childName SeedLoaderService.loadExistingRecords (packages/metadata-protocol/src/seed-loader.ts) — passes objectNameObjectQL.reportFindFailure — log level only, no data consequence
Severity, honestly
The failure direction is the loud one: a genuinely-unprovisioned federated remote surfaces as an error instead of being treated as truthful emptiness. Per the module's own docblock that is the cheap direction ("one extra error line" vs silent data loss), and #13324's ruling explicitly prefers it when the measurement is ambiguous. So this is a narrowing, not a corruption — but it IS a behaviour change for federated objects that nobody asked for, and it should be either closed or consciously accepted.
Not measured against a live federated deployment — derived from the mapping code and the call sites. Whether any shipped federated object also carries autonumber or inbound references (the two paths with a data consequence) is worth establishing before grading this.
Options, not decided here
- Let the predicate take the resolved physical name. The engine's
resolveObjectName does not consult the driver's physicalTableByObject, so this needs a driver-side accessor. - Let the driver declare the table it targeted on the error envelope.
backendStatementFaultError(object, cause) already exists in packages/drivers/driver-sql/src/sql-driver.ts and knows both names; the predicate would prefer a declared name over the caller's. This is the cleanest shape and removes the parameter from the narrow paths entirely, but it touches this lane's serial hot file and only helps drivers that adopt it. - Accept and document: for external objects, a missing remote surfaces loudly.
Generated by Claude Code
Generated by Claude Code
Found while implementing #13324 (PR #13437); out of that card's scope, filed unassigned for triage. It is a residual of that repair, not a pre-existing defect.
What #13324 changed
isMissingTableError(error, readObject)now refuses the benign "table not provisioned yet" verdict when the dialect phrase names a relation other than the one the caller read. Call sites pass the object API name they were reading.The gap
For a federated (external) object, the object API name is not the name the driver puts in the SQL.
SqlDriver.registerExternalObjectrecordsexternal.remoteNameinphysicalTableByObject, andgetBuildertargets that:So when a federated object declares
external: { remoteName: 'legacy_orders' }and that remote table is genuinely absent, the driver raises a phrase naminglegacy_orderswhile the call site declares the object name (saycrm_order). The names do not match, so the repaired predicate answers not benign for what is in fact a genuine missing table.The comparison folds away schema/database qualifiers, the legacy
namespace__shortprefix and case, but it cannot fold away an arbitraryremoteName— nothing at the call site knows the mapping, which lives on the driver instance.Reach
Only the call sites that read an arbitrary, caller-named object can hit this — a federated object is never one of the fixed
sys_*reads:ObjectQL.seedAutonumber(packages/objectql/src/engine.ts) — passesobjectchildNameSeedLoaderService.loadExistingRecords(packages/metadata-protocol/src/seed-loader.ts) — passesobjectNameObjectQL.reportFindFailure— log level only, no data consequenceSeverity, honestly
The failure direction is the loud one: a genuinely-unprovisioned federated remote surfaces as an error instead of being treated as truthful emptiness. Per the module's own docblock that is the cheap direction ("one extra error line" vs silent data loss), and #13324's ruling explicitly prefers it when the measurement is ambiguous. So this is a narrowing, not a corruption — but it IS a behaviour change for federated objects that nobody asked for, and it should be either closed or consciously accepted.
Not measured against a live federated deployment — derived from the mapping code and the call sites. Whether any shipped federated object also carries autonumber or inbound references (the two paths with a data consequence) is worth establishing before grading this.
Options, not decided here
resolveObjectNamedoes not consult the driver'sphysicalTableByObject, so this needs a driver-side accessor.backendStatementFaultError(object, cause)already exists inpackages/drivers/driver-sql/src/sql-driver.tsand knows both names; the predicate would prefer a declared name over the caller's. This is the cleanest shape and removes the parameter from the narrow paths entirely, but it touches this lane's serial hot file and only helps drivers that adopt it.Generated by Claude Code
Generated by Claude Code