Skip to content

FMOData: inconsistent error handling for invalid table/field references #147

@eluce2

Description

@eluce2

Summary

When using FMServerConnection with useEntityIds: true, two error scenarios produce inconsistent or unhelpful behavior compared to other error paths (bad credentials, unreachable server, missing record, etc.), which all correctly return { data, error } tuples.

Issue 1: Non-existent table throws instead of returning { error }

When querying a table defined with fmTableOccurrence() that doesn't exist on the server (e.g. entity IDs that don't match anything), calling .execute() throws an error:

Error: useEntityIds is true but table "TableName" does not have entity IDs configured

Stack trace points to resolveTableId in client/builders/table-utils.jsbuild in client/query/url-builder.jsexecute in client/query/query-builder.js.

Expected behavior: .execute() should return { error } like all other error conditions (auth errors, network errors, missing records, missing databases). The current behavior forces callers to both try/catch and check { error } to handle all failure modes, which defeats the purpose of the result tuple pattern.

Issue 2: Standalone field reference in eq() serializes as [object Object]

When a field created with textField().entityId(...) is used in a filter via eq() without being part of a table schema, it serializes as [object Object] in the OData URL:

$filter=[object Object] eq 'test'

This produces a server-side syntax error (code -1002), but the real problem is on the client side — there's no validation that the field reference is actually resolvable before building the URL.

Expected behavior: Either:

  1. Validate at URL-build time that the field reference can be resolved, and return a clear error (e.g., "field is not attached to a table occurrence"), or
  2. If possible, catch this at the type level so eq() only accepts fields from a defined table schema

Option 2 would be ideal since it would catch the mistake at compile time.

Environment

  • @proofkit/fmodata version: 0.1.0-beta.32 (from PR package @141)
  • useEntityIds: true
  • Retries set to 0 for testing clarity

Reproduction

Both issues are straightforward to reproduce with any FMServerConnection:

import { FMServerConnection, fmTableOccurrence, textField, eq } from "@proofkit/fmodata";

const connection = new FMServerConnection({ serverUrl: "...", auth: { username: "...", password: "..." } });
const db = connection.database("SomeFile.fmp12", { useEntityIds: true });

// Issue 1: throws instead of returning { error }
const FakeTable = fmTableOccurrence("NonExistent", {
  id: textField().primaryKey().entityId("FMFID:999999999"),
});
const result1 = await db.from(FakeTable).list().execute(); // throws!

// Issue 2: [object Object] in URL
const looseField = textField().entityId("FMFID:000000001");
const result2 = await db.from(RealTable).list().where(eq(looseField, "test")).execute();
// sends: $filter=[object Object] eq 'test'

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