Skip to content

openapi.json 500 "Unrecognized key" when a table reference uses apiSettable: false (Zod v4 .omit throws) #1

Description

@faskan

Summary

GET /api/openapi.json returns 500 {"error":"Unrecognized key: \"invoice_id\"","code":"INTERNAL"} whenever a table's meta.references declares a reference with apiSettable: false. Because sapporta endpoints list fetches /api/openapi.json, the CLI's endpoint discovery fails with the same error. Runtime table CRUD is unaffected — only OpenAPI / JSON-schema generation breaks.

Environment

  • @sapporta/server 0.3.0
  • zod 4.4.3
  • sapporta CLI 0.2.6
  • Node 24, project scaffolded via npx sapporta init

Reproduction

Define a sapportaTable whose meta marks a foreign-key reference as non-settable:

export const invoiceLines = sapportaTable({
  drizzle: invoiceLinesTable,
  meta: {
    label: "Invoice Lines",
    rowScope: "workspaceGlobal",
    rowLabelColumns: ["description"],
    references: {
      invoice_id: { table: "invoices", column: "id", apiSettable: false },
      product_id: { table: "products", column: "id" },
    },
  },
});

Boot the app, then:

GET /api/openapi.json      -> 500 Unrecognized key: "invoice_id"
sapporta endpoints list    -> Error: Unrecognized key: "invoice_id"  (code INTERNAL)

Removing apiSettable: false from the reference makes /api/openapi.json return 200 (36 paths) and endpoints list work.

Root cause

OpenAPI generation converts the table's Zod schema with Zod v4 z.toJSONSchema, which lazily evaluates the .shape getter of an .omit(mask) result. The generated write schema already excludes the apiSettable: false FK column, and then .omit() is applied for that same key. In Zod v4 (unlike v3) .omit() throws Unrecognized key: "<k>" when a mask key is not present in the object's shape:

// zod/v4/core/util.js  (get shape)
for (const key in mask) {
  if (!(key in currDef.shape)) {
    throw new Error(`Unrecognized key: "${key}"`);
  }
  ...
}

Non-reference apiWritable: false columns do not trigger this (their key is still present when omitted); only apiSettable: false references do.

Stack (server)

Error: Unrecognized key: "invoice_id"
  at get shape (zod/v4/core/util.js:376)
  at Module.objectProcessor (zod/v4/core/json-schema-processors.js:280)
  at process (zod/v4/core/to-json-schema.js:60)
  ... (arrayProcessor / objectProcessor recursion) ...
GET /api/openapi.json -> 500

Impact

Any project that uses apiSettable: false on a reference (a natural choice for server-managed FKs in master/detail tables) has a broken /api/openapi.json and non-functional sapporta endpoints / OpenAPI tooling.

Workaround

Remove apiSettable: false from reference metadata (which gives up generated-CRUD FK write-protection).

Suggested fix

Guard the omit in the schema-extraction path so it only omits keys actually present in the object's shape (or filter FK exclusions before building the omit mask), so it stays compatible with Zod v4's stricter .omit().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions