toeverything/AFFiNE · error · Error

[Table(${tableName})]: There should be at least one field ma

Error message

[Table(${tableName})]: There should be at least one field marked as primary key.

What it means

Schema validator PrimaryKeyShouldExist runs when a table definition is registered and throws if not a single field has isPrimaryKey set. Every AFFiNE ORM table must declare exactly one primary key so rows can be addressed for get/update/delete. This fails at definition time, before any data operation.

Source

Thrown at packages/common/infra/src/orm/core/validators/schema.ts:7

import type { TableSchemaValidator } from './types';

export const tableSchemaValidators: Record<string, TableSchemaValidator> = {
  PrimaryKeyShouldExist: {
    validate(tableName, table) {
      if (!Object.values(table).some(field => field.schema.isPrimaryKey)) {
        throw new Error(
          `[Table(${tableName})]: There should be at least one field marked as primary key.`
        );
      }
    },
  },
  OnlyOnePrimaryKey: {
    validate(tableName, table) {
      const primaryFields = [];

      for (const name in table) {
        if (table[name].schema.isPrimaryKey) {
          primaryFields.push(name);
        }
      }

      if (primaryFields.length > 1) {
        throw new Error(
          `[Table(${tableName})]: There should be only one field marked as primary key. Found [${primaryFields.join(', ')}].`

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Add .primaryKey() to the field that uniquely identifies rows (usually an id column).
  2. If no natural unique column exists, add an id: t.string().primaryKey() and populate it (e.g. with nanoid/uuid) on create.
  3. Re-run; this throws eagerly at table registration, so the stack points straight at the offending definition.

Example fix

// before
export const Setting = t.table('setting', {
  key: t.string(),
  value: t.string(),
});

// after
export const Setting = t.table('setting', {
  key: t.string().primaryKey(),
  value: t.string(),
});
Defensive patterns

Strategy: validation

Validate before calling

function assertSinglePrimaryKey(tableName: string, fields: Record<string, { isPrimaryKey?: boolean }>) {
  const pks = Object.entries(fields).filter(([, f]) => f.isPrimaryKey);
  if (pks.length === 0) throw new Error(`${tableName}: missing primary key`);
  if (pks.length > 1) throw new Error(`${tableName}: multiple primary keys ${pks.map(([n]) => n)}`);
}

Try / catch

try { registerTable(MyTable); } catch (e) { if (e instanceof Error && e.message.includes('primary key')) { /* fix definition; fails fast at startup */ } throw e; }

Prevention

When it happens

Trigger: Calling t.table('name', {...}) where no field uses .primaryKey(); defining the table with only plain t.string()/t.number() fields; forgetting .primaryKey() when copying an existing table definition.

Common situations: Creating a new table quickly during prototyping and skipping the key; refactoring a table and accidentally dropping the .primaryKey() chain; generating table definitions from a template that lacks the marker.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/12af8699dbb92a45. Report an issue: GitHub.