Skip to content

isMissingTableError's read-table check compares an object API name against a federated object's remoteName — a genuinely absent external remote now reads as loud, not benign #13438

Description

@claude

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 objectName
  • ObjectQL.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

  1. 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.
  2. 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.
  3. Accept and document: for external objects, a missing remote surfaces loudly.

Generated by Claude Code


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions